Compare commits

...
Sign in to create a new pull request.

18 commits

Author SHA1 Message Date
David Hain
c1a32c1a30
fix: README 2024-01-26 14:41:37 +01:00
David Hain
adc971f587
Update docker-compose.yml 2023-02-07 09:10:31 +01:00
Stefan Prechtl
28a3c48cdb
Update README.md 2023-02-05 22:48:26 +01:00
7e2ac3f488 Merge 2023-02-01 19:00:46 +01:00
6c80829a0b Merge remote-tracking branch 'origin/develop' into develop 2023-01-31 10:12:33 +01:00
41223dd786 README.md updated. 2023-01-31 10:12:22 +01:00
dhain
e06ffa51a3 Merge remote-tracking branch 'origin/develop' into develop 2023-01-31 08:52:37 +01:00
dhain
5dfbc6d39c User Scores at the top of the page are not displayed anymore after logout 2023-01-31 08:52:30 +01:00
ad117b4d2c Dockerized frontend 2023-01-31 08:37:02 +01:00
dhain
f4eeb11c68 Merge remote-tracking branch 'origin/develop' into develop
# Conflicts:
#	frontend/src/components/Login.vue
2023-01-31 08:35:03 +01:00
dhain
c2bb6c104a Fixed game sending player stats when not logged in 2023-01-31 08:31:40 +01:00
6a0e200905 Login input css improved, this.user removed since it was unnecessary 2023-01-24 11:33:13 +01:00
2ce3ed9cd7 Fixed leaderboard next when users dividable by pagesize 2023-01-24 10:54:17 +01:00
438184f759 Fixed leaderboard next when users dividable by pagesize 2023-01-24 10:53:09 +01:00
6a94e967e1 leaderboard better 2023-01-24 10:38:12 +01:00
dhain
3befee6fdd removed p5_loading div
(display: none and no remove)
2023-01-24 09:31:13 +01:00
j-weissen
dc3c2e0982 Merge branch 'logout-button' into develop 2023-01-24 09:23:44 +01:00
4c7875aa0d woke worke 2023-01-24 08:29:32 +01:00
15 changed files with 264 additions and 364 deletions

View file

@ -1,5 +1,6 @@
POSTGRES_PORT=5432 POSTGRES_PORT=5432
EXPRESS_PORT=3000 EXPRESS_PORT=3000
FRONTEND_PORT=8080
POSTGRES_USER=postgres POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres POSTGRES_PASSWORD=postgres

183
README.md
View file

@ -1,163 +1,30 @@
# RaspberryRocketeer # RaspberryRocketeer
## Class Diagram of all classes ## How to run
```mermaid ### Copy .env
classDiagram First you need to copy the `.env.example`.
direction BT
class Collidable {
collides(o: Entity) boolean
}
class Database {
any _db
any db
}
class Entity {
constructor(position: Position, width: number, height: number, fill: number)
Position _position
number _width
number _height
number fill
boolean _showHitbox
update() void
draw() void
Position position
number width
number height
boolean showHitbox
}
class Game {
number id
number score
string playtime
Date date
number userId
}
class GamePgPromiseRepository {
insert(game: Game) Promise~Game~
serialize(raw: any) Game
}
class GameRepository {
insert(game: Game) Promise~Game~
}
class HighscoreLeaderboard
class HighscoreLeaderboardPgPromiseRepository {
getAll() Promise~HighscoreLeaderboard~
serialize(raw: any) HighscoreLeaderboard
}
class HighscoreLeaderboardRepository {
getAll() Promise~HighscoreLeaderboard~
}
class Leaderboard
class LeaderboardEntry {
number username
number rank
T score
}
class Obstacle {
constructor(position: Position, obstacleWidth: number, obstacleHeight: number, pipeImagePath: string)
Pipe pipeTop
Pipe pipeBottom
number padding
number speed
number _distanceBetweenPipes
number _startX
resetPosition() void
randomizeHeight() void
randomRange(min: number, max: number) number
update() void
draw() void
collides(o: Entity) boolean
any startX
any distanceBetweenPipes
}
class Pipe {
constructor(positionX: number, width: number, height: number)
any _image
update() void
draw() void
move(speed: number) void
collides(o: Entity) boolean
any image
}
class Position {
constructor(x: number, y: number)
number _x
number _y
number x
number y
}
class Raspberry {
constructor(image: string)
number lift
number gravity
number _velocity
any _image
Position position
number maxVelocity
number WIDTH
number HEIGHT
number FILL
update() void
applyGravity() void
forceBoundaries() void
boost() void
draw() void
number velocity
any image
}
class TimeLeaderboard
class TimeLeaderboardPgPromiseRepository {
getAll() Promise~TimeLeaderboard~
serialize(raw: any) TimeLeaderboard
}
class TimeLeaderboardRepository {
getAll() Promise~TimeLeaderboard~
}
class User {
number id
string name
}
class UserPgPromiseRepository {
getById(id: number) Promise~User~
getByName(name: string) Promise~User~
withIdExists(id: number) Promise~boolean~
withNameExists(name: string) Promise~boolean~
insert(user: Omit~User, "id"~) Promise~User~
serialize(raw: any) User
}
class UserRepository {
getById(id: number) Promise~User~
getByName(name: string) Promise~User~
withIdExists(userId: number) Promise~boolean~
withNameExists(username: string) Promise~boolean~
insert(user: Omit~User, "id"~) Promise~User~
}
class UserScores {
number userId
number highscore
number totalScore
string totalPlaytime
number averageScore
number gamesPlayed
}
class UserScoresPgPromiseRepository {
getById(id: number) Promise~UserScores~
serialize(raw: any) UserScores
}
class UserScoresRepository {
getById(userId: number) Promise~UserScores~
}
GamePgPromiseRepository --> GameRepository
HighscoreLeaderboardPgPromiseRepository --> HighscoreLeaderboardRepository
Obstacle ..> Collidable
Obstacle --> Entity
Pipe ..> Collidable
Pipe --> Entity
Raspberry --> Entity
TimeLeaderboardPgPromiseRepository --> TimeLeaderboardRepository
UserPgPromiseRepository --> UserRepository
UserScoresPgPromiseRepository --> UserScoresRepository
```shell
cp .env.example .env
``` ```
<small>Note: It is recommended to change the values for the database user.</small>
### Install node packages
Go into the frontend folder using
```shell
cd frontend
```
and run:
```shell
npm install
```
### Start the container (in the project root)
```shell
docker compose up --build
```
You can then access the website on `localhost:8080`

View file

@ -12,7 +12,7 @@ export class HighscoreLeaderboardPgPromiseRepository extends HighscoreLeaderboar
async getPage(entriesPerPage, page): Promise<HighscoreLeaderboard> { async getPage(entriesPerPage, page): Promise<HighscoreLeaderboard> {
const raw: any = await Database.db.manyOrNone( const raw: any = await Database.db.manyOrNone(
'SELECT * FROM lb_highscore INNER JOIN "user" ON user_id = id ORDER BY rank LIMIT $1 OFFSET $2*$1;', 'SELECT * FROM lb_highscore INNER JOIN "user" ON user_id = id ORDER BY rank LIMIT $1 OFFSET $2;',
[entriesPerPage, page * entriesPerPage] [entriesPerPage, page * entriesPerPage]
); );
return this.serialize(raw); return this.serialize(raw);

View file

@ -12,7 +12,7 @@ export class TimeLeaderboardPgPromiseRepository extends TimeLeaderboardRepositor
async getPage(entriesPerPage: number, page: number): Promise<TimeLeaderboard> { async getPage(entriesPerPage: number, page: number): Promise<TimeLeaderboard> {
const raw: any = await Database.db.manyOrNone( const raw: any = await Database.db.manyOrNone(
'SELECT * FROM lb_total_playtime INNER JOIN "user" ON user_id = id ORDER BY rank LIMIT $1 OFFSET $2*$1;', 'SELECT * FROM lb_total_playtime INNER JOIN "user" ON user_id = id ORDER BY rank LIMIT $1 OFFSET $2;',
[entriesPerPage, page * entriesPerPage] [entriesPerPage, page * entriesPerPage]
); );
return this.serialize(raw); return this.serialize(raw);

View file

@ -1,40 +0,0 @@
insert into "user" (name) values ('dpettus0');
insert into "user" (name) values ('egreetland1');
insert into "user" (name) values ('smontford2');
insert into "user" (name) values ('idagwell3');
insert into "user" (name) values ('lgagan4');
insert into "user" (name) values ('acarmont5');
insert into "user" (name) values ('kjermyn6');
insert into "user" (name) values ('dokieran7');
insert into "user" (name) values ('pdrinkel8');
insert into game (user_id, score, playtime, date) values ('1', 74, '19:59', '2022-07-19');
insert into game (user_id, score, playtime, date) values ('1', 86, '20:32', '2022-11-24');
insert into game (user_id, score, playtime, date) values ('1', 68, '10:41', '2022-03-24');
insert into game (user_id, score, playtime, date) values ('2', 39, '5:55', '2022-06-01');
insert into game (user_id, score, playtime, date) values ('2', 20, '9:23', '2022-03-12');
insert into game (user_id, score, playtime, date) values ('2', 28, '23:45', '2022-04-01');
insert into game (user_id, score, playtime, date) values ('2', 44, '18:43', '2022-06-24');
insert into game (user_id, score, playtime, date) values ('3', 92, '14:54', '2022-11-06');
insert into game (user_id, score, playtime, date) values ('3', 73, '0:45', '2022-07-26');
insert into game (user_id, score, playtime, date) values ('3', 27, '2:49', '2022-02-03');
insert into game (user_id, score, playtime, date) values ('4', 26, '2:32', '2022-07-19');
insert into game (user_id, score, playtime, date) values ('4', 12, '17:03', '2022-04-25');
insert into game (user_id, score, playtime, date) values ('4', 6, '8:49', '2021-12-03');
insert into game (user_id, score, playtime, date) values ('4', 22, '22:27', '2022-03-02');
insert into game (user_id, score, playtime, date) values ('5', 94, '1:04', '2022-10-19');
insert into game (user_id, score, playtime, date) values ('5', 2, '2:14', '2022-04-06');
insert into game (user_id, score, playtime, date) values ('5', 21, '17:18', '2022-06-03');
insert into game (user_id, score, playtime, date) values ('6', 33, '16:01', '2022-02-02');
insert into game (user_id, score, playtime, date) values ('6', 27, '7:03', '2022-02-06');
insert into game (user_id, score, playtime, date) values ('6', 62, '0:45', '2022-11-15');
insert into game (user_id, score, playtime, date) values ('7', 12, '8:54', '2022-06-29');
insert into game (user_id, score, playtime, date) values ('7', 63, '16:01', '2022-11-05');
insert into game (user_id, score, playtime, date) values ('7', 29, '0:46', '2022-10-01');
insert into game (user_id, score, playtime, date) values ('8', 67, '1:27', '2022-09-29');
insert into game (user_id, score, playtime, date) values ('8', 84, '10:37', '2021-12-18');
insert into game (user_id, score, playtime, date) values ('8', 14, '19:14', '2022-01-31');
insert into game (user_id, score, playtime, date) values ('9', 21, '19:04', '2022-03-08');
insert into game (user_id, score, playtime, date) values ('9', 46, '2:34', '2022-04-18');
insert into game (user_id, score, playtime, date) values ('9', 78, '9:33', '2022-09-10');
insert into game (user_id, score, playtime, date) values ('9', 82, '11:19', '2022-11-29');

View file

@ -21,3 +21,12 @@ services:
ports: ports:
- "${EXPRESS_PORT}:3000" - "${EXPRESS_PORT}:3000"
vue:
build: frontend
container_name: frontend
ports:
- "${FRONTEND_PORT}:8080"
volumes:
- ./frontend:/app

3
frontend/.dockerignore Normal file
View file

@ -0,0 +1,3 @@
node_modules
.gitignore
README.md

11
frontend/Dockerfile Normal file
View file

@ -0,0 +1,11 @@
FROM node:18
COPY . /app
WORKDIR /app
RUN npm install
EXPOSE 8080
ENTRYPOINT ["npm", "run", "serve"]

View file

@ -10,11 +10,11 @@
}, },
"dependencies": { "dependencies": {
"bootstrap": "^5.2.3", "bootstrap": "^5.2.3",
"vue": "^3.2.13" "vue": "^3.2.13",
"@vue/cli-service": "~5.0.0"
}, },
"devDependencies": { "devDependencies": {
"@vue/cli-plugin-typescript": "~5.0.0", "@vue/cli-plugin-typescript": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"typescript": "~4.5.5" "typescript": "~4.5.5"
} }
} }

View file

@ -1,20 +1,20 @@
var __extends = (this && this.__extends) || (function () { var __extends = (this && this.__extends) || (function(){
var extendStatics = function (d, b) { var extendStatics = function(d, b){
extendStatics = Object.setPrototypeOf || extendStatics = Object.setPrototypeOf ||
({__proto__: []} instanceof Array && function (d, b) { ({__proto__: []} instanceof Array && function(d, b){
d.__proto__ = b; d.__proto__ = b;
}) || }) ||
function (d, b) { function(d, b){
for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; for(var p in b) if(Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p];
}; };
return extendStatics(d, b); return extendStatics(d, b);
}; };
return function (d, b) { return function(d, b){
if (typeof b !== "function" && b !== null) if(typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b); extendStatics(d, b);
function __() { function __(){
this.constructor = d; this.constructor = d;
} }
@ -45,21 +45,21 @@ var hasAlreadyScored = false;
var hasDied = true; var hasDied = true;
var ready = true; var ready = true;
function preload() { function preload(){
font = loadFont(FONT_PATH); font = loadFont(FONT_PATH);
backgroundImage = loadImage(BACKGROUND_IMAGE_PATH); backgroundImage = loadImage(BACKGROUND_IMAGE_PATH);
pipeImage = loadImage(PIPE_IMAGE_PATH); pipeImage = loadImage(PIPE_IMAGE_PATH);
floorImage = loadImage(FLOOR_IMAGE_PATH); floorImage = loadImage(FLOOR_IMAGE_PATH);
} }
function setup() { function setup(){
createCanvas(1085, 600); createCanvas(1085, 600);
floorHeight = height / 5; floorHeight = height / 5;
setupObstacleConsts(); setupObstacleConsts();
setupFont(); setupFont();
setupGame(); setupGame();
var originalSetItem = localStorage.setItem; var originalSetItem = localStorage.setItem;
localStorage.setItem = function (key, value) { localStorage.setItem = function(key, value){
var event = new Event('itemInserted'); var event = new Event('itemInserted');
event.value = value; // Optional.. event.value = value; // Optional..
@ -69,57 +69,57 @@ function setup() {
}; };
} }
function setupObstacleConsts() { function setupObstacleConsts(){
obstacleOffset = width / OBSTACLE_COUNT; obstacleOffset = width / OBSTACLE_COUNT;
obstacleWidth = width / 22.727272727272727272; obstacleWidth = width / 22.727272727272727272;
Obstacle.distanceBetweenPipes = height / 2.5; Obstacle.distanceBetweenPipes = height / 2.5;
Obstacle.startX = width; Obstacle.startX = width;
} }
function setupFont() { function setupFont(){
textSize(75); textSize(75);
textAlign(CENTER); textAlign(CENTER);
textFont(font); textFont(font);
} }
function setupGame() { function setupGame(){
paused = true; paused = true;
raspberry = new Raspberry(RASPBERRY_IMAGE_PATH); raspberry = new Raspberry(RASPBERRY_IMAGE_PATH);
setupObstacles(); setupObstacles();
} }
function setupObstacles() { function setupObstacles(){
obstacles = []; obstacles = [];
instantiateObstacles(OBSTACLE_COUNT); instantiateObstacles(OBSTACLE_COUNT);
obstacles.forEach(function (obstacle) { obstacles.forEach(function(obstacle){
return obstacle.randomizeHeight(); return obstacle.randomizeHeight();
}); });
} }
function instantiateObstacles(number) { function instantiateObstacles(number){
for (var i = 0; i < number; i++) { for(var i = 0; i < number; i++){
obstacles.push(new Obstacle(new Position(width + obstacleOffset * i, 0), obstacleWidth, height, pipeImage)); obstacles.push(new Obstacle(new Position(width + obstacleOffset * i, 0), obstacleWidth, height, pipeImage));
} }
} }
function draw() { function draw(){
update(); update();
gameLoop(); gameLoop();
drawGame(); drawGame();
} }
function drawGame() { function drawGame(){
drawScenery(); drawScenery();
drawEntities(); drawEntities();
displayScore(); displayScore();
} }
function drawScenery() { function drawScenery(){
background(backgroundImage); background(backgroundImage);
drawFloor(); drawFloor();
} }
function drawFloor() { function drawFloor(){
push(); push();
noFill(); noFill();
image(floorImage, 0, height - floorHeight, width, floorHeight); image(floorImage, 0, height - floorHeight, width, floorHeight);
@ -127,68 +127,71 @@ function drawFloor() {
pop(); pop();
} }
function drawEntities() { function drawEntities(){
raspberry.draw(); raspberry.draw();
drawObstacles(); drawObstacles();
} }
function drawObstacles() { function drawObstacles(){
obstacles.forEach(function (obstacle) { obstacles.forEach(function(obstacle){
obstacle.draw(); obstacle.draw();
}); });
} }
function gameLoop() { function gameLoop(){
if (!paused) { if(!paused){
collisionCheck(obstacles[0]); collisionCheck(obstacles[0]);
checkRaspberryScore(); checkRaspberryScore();
} }
} }
function collisionCheck(o) { function collisionCheck(o){
if (o.collides(raspberry)) { if(o.collides(raspberry)){
die(); die();
setupGame(); setupGame();
} }
} }
function die() { function die(){
if(localStorage.getItem("frontend-ready") == "false")
return;
ready = false; ready = false;
hasDied = true; hasDied = true;
playTime = Date.now() - startTime; playTime = Date.now() - startTime;
exportToLocalStorage(); exportToLocalStorage();
setTimeout(function () { setTimeout(function(){
return ready = true; return ready = true;
}, 1000); }, 1000);
} }
function exportToLocalStorage() { function exportToLocalStorage(){
localStorage.setItem("game-playTime", String(playTime)); localStorage.setItem("game-playTime", String(playTime));
localStorage.setItem("game-score", String(score)); localStorage.setItem("game-score", String(score));
localStorage.setItem("game-isRunning", String(!hasDied)); localStorage.setItem("game-isRunning", String(!hasDied));
} }
function displayScore() { function displayScore(){
push(); push();
fill(195, 33, 34); fill(195, 33, 34);
text(score, 0, height / 8, width, height); text(score, 0, height / 8, width, height);
pop(); pop();
} }
function update() { function update(){
if (!paused) { if(!paused){
raspberry.update(); raspberry.update();
} }
obstacles.forEach(function (obstacle) { obstacles.forEach(function(obstacle){
if (!paused) { if(!paused){
obstacle.update(); obstacle.update();
checkObstacleReset(obstacle); checkObstacleReset(obstacle);
} }
}); });
} }
function checkObstacleReset(obstacle) { function checkObstacleReset(obstacle){
if (obstacle.position.x < -obstacleWidth) { if(obstacle.position.x < -obstacleWidth){
obstacle.resetPosition(); obstacle.resetPosition();
obstacles.shift(); obstacles.shift();
obstacles.push(obstacle); obstacles.push(obstacle);
@ -196,40 +199,41 @@ function checkObstacleReset(obstacle) {
} }
} }
function checkRaspberryScore() { function checkRaspberryScore(){
if ((obstacles[0].position.x + obstacles[0].width / 2) < (raspberry.position.x + raspberry.width / 2) if((obstacles[0].position.x + obstacles[0].width / 2) < (raspberry.position.x + raspberry.width / 2)
&& !hasAlreadyScored) { && !hasAlreadyScored){
score += 1; score += 1;
hasAlreadyScored = true; hasAlreadyScored = true;
} }
} }
function resetScore() { function resetScore(){
if (hasDied) { if(!hasDied || localStorage.getItem("frontend-ready") == "false")
return;
hasDied = false; hasDied = false;
score = 0; score = 0;
hasAlreadyScored = false; hasAlreadyScored = false;
startTime = Date.now(); startTime = Date.now();
exportToLocalStorage(); exportToLocalStorage();
}
} }
function keyPressed() { function keyPressed(){
if (!ready) if(!ready)
return; return;
if (BOOST_KEYS.includes(key.toLowerCase())) { if(BOOST_KEYS.includes(key.toLowerCase())){
resetScore(); resetScore();
raspberry.boost(); raspberry.boost();
} }
if (key == "Escape") { if(key == "Escape"){
paused = !paused; paused = !paused;
} else if (paused) { } else if(paused){
paused = false; paused = false;
} }
} }
var Entity = (function () { var Entity = (function(){
function Entity(position, width, height, fill) { function Entity(position, width, height, fill){
this.position = position; this.position = position;
this.width = width; this.width = width;
this.height = height; this.height = height;
@ -238,46 +242,46 @@ var Entity = (function () {
} }
Object.defineProperty(Entity.prototype, "position", { Object.defineProperty(Entity.prototype, "position", {
get: function () { get: function(){
return this._position; return this._position;
}, },
set: function (value) { set: function(value){
this._position = value; this._position = value;
}, },
enumerable: false, enumerable: false,
configurable: true configurable: true
}); });
Object.defineProperty(Entity.prototype, "width", { Object.defineProperty(Entity.prototype, "width", {
get: function () { get: function(){
return this._width; return this._width;
}, },
set: function (value) { set: function(value){
this._width = value; this._width = value;
}, },
enumerable: false, enumerable: false,
configurable: true configurable: true
}); });
Object.defineProperty(Entity.prototype, "height", { Object.defineProperty(Entity.prototype, "height", {
get: function () { get: function(){
return this._height; return this._height;
}, },
set: function (value) { set: function(value){
this._height = value; this._height = value;
}, },
enumerable: false, enumerable: false,
configurable: true configurable: true
}); });
Object.defineProperty(Entity.prototype, "showHitbox", { Object.defineProperty(Entity.prototype, "showHitbox", {
get: function () { get: function(){
return this._showHitbox; return this._showHitbox;
}, },
set: function (value) { set: function(value){
this._showHitbox = value; this._showHitbox = value;
}, },
enumerable: false, enumerable: false,
configurable: true configurable: true
}); });
Entity.prototype.draw = function () { Entity.prototype.draw = function(){
push(); push();
fill(this.fill); fill(this.fill);
rect(this.position.x, this.position.y, this.width, this.height); rect(this.position.x, this.position.y, this.width, this.height);
@ -285,10 +289,10 @@ var Entity = (function () {
}; };
return Entity; return Entity;
}()); }());
var Obstacle = (function (_super) { var Obstacle = (function(_super){
__extends(Obstacle, _super); __extends(Obstacle, _super);
function Obstacle(position, obstacleWidth, obstacleHeight, image) { function Obstacle(position, obstacleWidth, obstacleHeight, image){
var _this = _super.call(this, position, obstacleWidth, obstacleHeight, 0) || this; var _this = _super.call(this, position, obstacleWidth, obstacleHeight, 0) || this;
_this.speed = 3; _this.speed = 3;
_this.padding = height / 6.6666666666666666; _this.padding = height / 6.6666666666666666;
@ -297,62 +301,62 @@ var Obstacle = (function (_super) {
} }
Object.defineProperty(Obstacle, "startX", { Object.defineProperty(Obstacle, "startX", {
set: function (value) { set: function(value){
this._startX = value; this._startX = value;
}, },
enumerable: false, enumerable: false,
configurable: true configurable: true
}); });
Object.defineProperty(Obstacle, "distanceBetweenPipes", { Object.defineProperty(Obstacle, "distanceBetweenPipes", {
set: function (value) { set: function(value){
this._distanceBetweenPipes = value; this._distanceBetweenPipes = value;
}, },
enumerable: false, enumerable: false,
configurable: true configurable: true
}); });
Obstacle.prototype.createPipes = function (position, obstacleHeight, obstacleWidth, pipeImage) { Obstacle.prototype.createPipes = function(position, obstacleHeight, obstacleWidth, pipeImage){
this.pipeTop = new Pipe(position.x, obstacleWidth, obstacleHeight, pipeImage); this.pipeTop = new Pipe(position.x, obstacleWidth, obstacleHeight, pipeImage);
this.pipeBottom = new Pipe(position.x, obstacleWidth, obstacleHeight, pipeImage); this.pipeBottom = new Pipe(position.x, obstacleWidth, obstacleHeight, pipeImage);
}; };
Obstacle.prototype.resetPosition = function () { Obstacle.prototype.resetPosition = function(){
this.randomizeHeight(); this.randomizeHeight();
this.pipeBottom.position.x = Obstacle._startX; this.pipeBottom.position.x = Obstacle._startX;
this.pipeTop.position.x = Obstacle._startX; this.pipeTop.position.x = Obstacle._startX;
}; };
Obstacle.prototype.randomizeHeight = function () { Obstacle.prototype.randomizeHeight = function(){
this.pipeTop.height = this.randomRange(this.padding, height - this.padding - Obstacle._distanceBetweenPipes); this.pipeTop.height = this.randomRange(this.padding, height - this.padding - Obstacle._distanceBetweenPipes);
this.pipeBottom.position.y = this.pipeTop.height + Obstacle._distanceBetweenPipes; this.pipeBottom.position.y = this.pipeTop.height + Obstacle._distanceBetweenPipes;
this.pipeBottom.height = height - this.pipeTop.height - this.padding; this.pipeBottom.height = height - this.pipeTop.height - this.padding;
}; };
Obstacle.prototype.randomRange = function (min, max) { Obstacle.prototype.randomRange = function(min, max){
return Math.random() * (max - min) + min; return Math.random() * (max - min) + min;
}; };
Obstacle.prototype.update = function () { Obstacle.prototype.update = function(){
this.pipeTop.move(this.speed); this.pipeTop.move(this.speed);
this.pipeBottom.move(this.speed); this.pipeBottom.move(this.speed);
this.position.x = this.pipeTop.position.x; this.position.x = this.pipeTop.position.x;
}; };
Obstacle.prototype.draw = function () { Obstacle.prototype.draw = function(){
this.pipeTop.draw(); this.pipeTop.draw();
this.pipeBottom.draw(); this.pipeBottom.draw();
}; };
Obstacle.prototype.collides = function (o) { Obstacle.prototype.collides = function(o){
return this.pipeTop.collides(o) || this.pipeBottom.collides(o); return this.pipeTop.collides(o) || this.pipeBottom.collides(o);
}; };
return Obstacle; return Obstacle;
}(Entity)); }(Entity));
var Pipe = (function (_super) { var Pipe = (function(_super){
__extends(Pipe, _super); __extends(Pipe, _super);
function Pipe(positionX, width, height, image) { function Pipe(positionX, width, height, image){
var _this = _super.call(this, new Position(positionX, 0), width, height, 0) || this; var _this = _super.call(this, new Position(positionX, 0), width, height, 0) || this;
_this.image = image; _this.image = image;
return _this; return _this;
} }
Pipe.prototype.update = function () { Pipe.prototype.update = function(){
}; };
Pipe.prototype.draw = function () { Pipe.prototype.draw = function(){
push(); push();
noFill(); noFill();
var imageAspectRatio = this.image.height / this.image.width; var imageAspectRatio = this.image.height / this.image.width;
@ -361,30 +365,30 @@ var Pipe = (function (_super) {
rect(this.position.x, this.position.y, this.width, this.height); rect(this.position.x, this.position.y, this.width, this.height);
pop(); pop();
}; };
Pipe.prototype.drawImage = function (computedImageHeight, imageAspectRatio) { Pipe.prototype.drawImage = function(computedImageHeight, imageAspectRatio){
if (this.height > computedImageHeight) { if(this.height > computedImageHeight){
var maxImageYPos = Math.ceil(this.height / computedImageHeight) * computedImageHeight; var maxImageYPos = Math.ceil(this.height / computedImageHeight) * computedImageHeight;
for (var imageYPosition = 0; imageYPosition < maxImageYPos; imageYPosition += computedImageHeight) { for(var imageYPosition = 0; imageYPosition < maxImageYPos; imageYPosition += computedImageHeight){
if (imageYPosition + computedImageHeight >= maxImageYPos) { if(imageYPosition + computedImageHeight >= maxImageYPos){
this.cropLastImage(imageYPosition, computedImageHeight, imageAspectRatio); this.cropLastImage(imageYPosition, computedImageHeight, imageAspectRatio);
break; break;
} }
image(this.image, this.position.x, this.position.y + imageYPosition, this.width, computedImageHeight); image(this.image, this.position.x, this.position.y + imageYPosition, this.width, computedImageHeight);
} }
} else { } else{
image(this.image, this.position.x, this.position.y, this.width, this.height); image(this.image, this.position.x, this.position.y, this.width, this.height);
} }
}; };
Pipe.prototype.cropLastImage = function (imageYPosition, computedImageHeight, imageAspectRatio) { Pipe.prototype.cropLastImage = function(imageYPosition, computedImageHeight, imageAspectRatio){
var amountOfImages = Math.floor(imageYPosition / computedImageHeight); var amountOfImages = Math.floor(imageYPosition / computedImageHeight);
var heightToEdge = this.height - (amountOfImages * computedImageHeight); var heightToEdge = this.height - (amountOfImages * computedImageHeight);
var croppedImage = this.image.get(0, 0, this.image.width, this.image.height - (heightToEdge * imageAspectRatio)); var croppedImage = this.image.get(0, 0, this.image.width, this.image.height - (heightToEdge * imageAspectRatio));
image(croppedImage, this.position.x, this.position.y + imageYPosition, this.width, heightToEdge); image(croppedImage, this.position.x, this.position.y + imageYPosition, this.width, heightToEdge);
}; };
Pipe.prototype.move = function (speed) { Pipe.prototype.move = function(speed){
this.position.x -= speed; this.position.x -= speed;
}; };
Pipe.prototype.collides = function (o) { Pipe.prototype.collides = function(o){
return this.position.x < (o.position.x + o.width) && return this.position.x < (o.position.x + o.width) &&
(this.position.x + this.width) > o.position.x && (this.position.x + this.width) > o.position.x &&
this.position.y < (o.position.y + o.height) && this.position.y < (o.position.y + o.height) &&
@ -392,27 +396,27 @@ var Pipe = (function (_super) {
}; };
return Pipe; return Pipe;
}(Entity)); }(Entity));
var Position = (function () { var Position = (function(){
function Position(x, y) { function Position(x, y){
this._x = x; this._x = x;
this._y = y; this._y = y;
} }
Object.defineProperty(Position.prototype, "x", { Object.defineProperty(Position.prototype, "x", {
get: function () { get: function(){
return this._x; return this._x;
}, },
set: function (value) { set: function(value){
this._x = value; this._x = value;
}, },
enumerable: false, enumerable: false,
configurable: true configurable: true
}); });
Object.defineProperty(Position.prototype, "y", { Object.defineProperty(Position.prototype, "y", {
get: function () { get: function(){
return this._y; return this._y;
}, },
set: function (value) { set: function(value){
this._y = value; this._y = value;
}, },
enumerable: false, enumerable: false,
@ -420,10 +424,10 @@ var Position = (function () {
}); });
return Position; return Position;
}()); }());
var Raspberry = (function (_super) { var Raspberry = (function(_super){
__extends(Raspberry, _super); __extends(Raspberry, _super);
function Raspberry(image) { function Raspberry(image){
var _this = this; var _this = this;
Raspberry.position = new Position(width / 6, height / 2); Raspberry.position = new Position(width / 6, height / 2);
Raspberry.height = height / 14.2857142857142857; Raspberry.height = height / 14.2857142857142857;
@ -438,73 +442,73 @@ var Raspberry = (function (_super) {
} }
Object.defineProperty(Raspberry.prototype, "velocity", { Object.defineProperty(Raspberry.prototype, "velocity", {
get: function () { get: function(){
return this._velocity; return this._velocity;
}, },
set: function (value) { set: function(value){
this._velocity = (Math.abs(this.velocity) > Raspberry.maxVelocity) ? Raspberry.maxVelocity : value; this._velocity = (Math.abs(this.velocity) > Raspberry.maxVelocity) ? Raspberry.maxVelocity : value;
}, },
enumerable: false, enumerable: false,
configurable: true configurable: true
}); });
Object.defineProperty(Raspberry.prototype, "image", { Object.defineProperty(Raspberry.prototype, "image", {
get: function () { get: function(){
return this._image; return this._image;
}, },
set: function (path) { set: function(path){
this._image = loadImage(path); this._image = loadImage(path);
}, },
enumerable: false, enumerable: false,
configurable: true configurable: true
}); });
Raspberry.prototype.update = function () { Raspberry.prototype.update = function(){
this.applyGravity(); this.applyGravity();
this.forceBoundaries(); this.forceBoundaries();
}; };
Raspberry.prototype.applyGravity = function () { Raspberry.prototype.applyGravity = function(){
this.velocity += this.gravity; this.velocity += this.gravity;
this.position.y += this.velocity; this.position.y += this.velocity;
}; };
Raspberry.prototype.forceBoundaries = function () { Raspberry.prototype.forceBoundaries = function(){
this.boundaryTop(); this.boundaryTop();
this.boundaryBottom(); this.boundaryBottom();
}; };
Raspberry.prototype.boundaryTop = function () { Raspberry.prototype.boundaryTop = function(){
if (this.position.y < 0) { if(this.position.y < 0){
this.position.y = 0; this.position.y = 0;
this.velocity = 0; this.velocity = 0;
} }
}; };
Raspberry.prototype.boundaryBottom = function () { Raspberry.prototype.boundaryBottom = function(){
if (this.position.y + this.height + Raspberry.bottomFloorOffset > height) { if(this.position.y + this.height + Raspberry.bottomFloorOffset > height){
this.position.y = height - this.height - Raspberry.bottomFloorOffset; this.position.y = height - this.height - Raspberry.bottomFloorOffset;
this.velocity = 0; this.velocity = 0;
} }
}; };
Raspberry.prototype.boost = function () { Raspberry.prototype.boost = function(){
this.velocity += this.lift; this.velocity += this.lift;
}; };
Raspberry.prototype.draw = function () { Raspberry.prototype.draw = function(){
push(); push();
noFill(); noFill();
this.setPose(); this.setPose();
this.drawObject(); this.drawObject();
pop(); pop();
}; };
Raspberry.prototype.drawObject = function () { Raspberry.prototype.drawObject = function(){
this.drawHitBox(); this.drawHitBox();
this.drawRocket(); this.drawRocket();
}; };
Raspberry.prototype.drawRocket = function () { Raspberry.prototype.drawRocket = function(){
image(this.image, 0, 0, this.width, this.height); image(this.image, 0, 0, this.width, this.height);
rect(0, 0, this.width, this.height); rect(0, 0, this.width, this.height);
}; };
Raspberry.prototype.drawHitBox = function () { Raspberry.prototype.drawHitBox = function(){
if (!this.showHitbox) { if(!this.showHitbox){
noStroke(); noStroke();
} }
}; };
Raspberry.prototype.setPose = function () { Raspberry.prototype.setPose = function(){
translate(this.position.x, this.position.y); translate(this.position.x, this.position.y);
rotate((PI / 2) * (this.velocity / Raspberry.maxVelocity)); rotate((PI / 2) * (this.velocity / Raspberry.maxVelocity));
}; };

View file

@ -13,7 +13,7 @@
<Login v-if="!user" @userChange="(event) => {this.updateUser(event);}"> <Login v-if="!user" @userChange="(event) => {this.updateUser(event);}">
</Login> </Login>
<div v-if="user" class="logout-wrapper offset-10 col-2"> <div v-if="user" class="logout-wrapper offset-10 col-2">
<RRButton @click="this.user = null; this.userId = -1;" text="Logout"></RRButton> <RRButton @click="logOut()" text="Logout"></RRButton>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
@ -59,7 +59,9 @@ export default defineComponent({
leaderboardEvent: new Event('reloadLeaderboard') leaderboardEvent: new Event('reloadLeaderboard')
} }
}, },
created() {
localStorage.setItem("frontend-ready", "false");
},
methods: { methods: {
async fetchFromApi(path: string, method: "GET" | "POST") { async fetchFromApi(path: string, method: "GET" | "POST") {
@ -80,6 +82,12 @@ export default defineComponent({
await this.updateUserScores(); await this.updateUserScores();
} }
window.dispatchEvent(this.leaderboardEvent); window.dispatchEvent(this.leaderboardEvent);
},
logOut(){
this.user = null;
this.userId = -1;
this.userScores = {};
localStorage.setItem('frontend-ready', 'false');
} }
}, },
}); });
@ -99,9 +107,11 @@ export default defineComponent({
.everything { .everything {
margin-bottom: 4em; margin-bottom: 4em;
} }
.hidden { .hidden {
visibility: hidden; visibility: hidden;
} }
.logout-wrapper { .logout-wrapper {
display: flex; display: flex;
justify-content: right; justify-content: right;

View file

@ -58,3 +58,9 @@ export default {
}, },
} }
</script> </script>
<style>
#p5_loading {
display: none;
}
</style>

View file

@ -9,9 +9,12 @@
<RRButton @click="nextPage" text=">"></RRButton> <RRButton @click="nextPage" text=">"></RRButton>
</div> </div>
</div> </div>
<div class="row" v-for="entry in this.page" :key="entry.rank"> <div class="row" v-if="this.page.length > 0" v-for="entry in this.page" :key="entry.rank">
<LeaderboardEntry :entry="entry"></LeaderboardEntry> <LeaderboardEntry :entry="entry"></LeaderboardEntry>
</div> </div>
<div class="row" v-else>
Loading...
</div>
</div> </div>
</template> </template>
@ -39,11 +42,7 @@ export default {
}, },
created() { created() {
this.updatePage(); this.updatePage();
window.addEventListener('itemInserted', (event) => { window.addEventListener('itemInserted', event => this.onItemInserted(event), false);
if (event.key === 'game-isRunning' && event.value === 'false'){
this.updatePage();
}
}, false)
}, },
methods: { methods: {
async fetchPage() { async fetchPage() {
@ -53,21 +52,33 @@ export default {
title() { title() {
return this.type === "totalplaytime" ? "Total Playtime" : "Highscore"; return this.type === "totalplaytime" ? "Total Playtime" : "Highscore";
}, },
nextPage() { async nextPage() {
if (this.page.length !== this.entriesPerPage) return;
this.pageNumber++; this.pageNumber++;
this.updatePage(); await this.updatePage();
if (this.page.length === 0) {
this.prevPage();
}
}, },
prevPage() { prevPage() {
if (this.pageNumber > 0) this.pageNumber--; if (this.pageNumber <= 0) return;
this.pageNumber--;
this.updatePage(); this.updatePage();
}, },
async updatePage() { async updatePage() {
let tempPage = await this.fetchPage(); let tempPage = await this.fetchPage();
for (let i = 0; i < this.entriesPerPage; i++) { for (let i = 0; i < this.entriesPerPage; i++) {
this.page.pop() this.page.pop();
} }
for (const entry of tempPage) { for (const entry of tempPage) {
this.page.push(entry) this.page.push(entry);
}
},
onItemInserted(event) {
if (event.key === 'game-isRunning' && event.value === 'false') {
this.updatePage();
} }
} }
} }

View file

@ -1,17 +1,18 @@
<template> <template>
<div class="col-1 text-left">{{this.entry.rank}}</div> <div class="col-1 text-right">{{this.entry.rank}}</div>
<div class="col text-left">{{this.entry.username}}</div> <span class="col offset-1 text-left username">{{this.entry.username}}</span>
<div class="col text-right">{{this.entry.score}}</div> <div class="col-2 text-right">{{this.entry.score}}</div>
</template> </template>
<script lang="ts"> <script lang="ts">
import {defineComponent} from 'vue'; import {defineComponent, PropType} from 'vue';
import {Leaderboard, LeaderboardEntry} from "@/model/Leaderboard";
export default defineComponent({ export default defineComponent({
name: "LeaderboardEntry", name: "LeaderboardEntry",
props: { props: {
entry: { entry: {
type: Object, type: Object as PropType<LeaderboardEntry<string>>,
default: () => ({}), default: () => ({}),
}, },
}, },
@ -22,4 +23,9 @@ export default defineComponent({
* { * {
font-size: 1.5em; font-size: 1.5em;
} }
.username {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
</style> </style>

View file

@ -1,7 +1,7 @@
<template> <template>
<h2>Enter a username</h2> <h2>Enter a username</h2>
<div class="form-floating mb-3"> <div class="form-floating mb-3">
<input type="email" class="form-control" id="floatingInput" placeholder="example name" v-model="username"> <input class="form-control" id="floatingInput" placeholder="example name" v-model="username">
<label for="floatingInput">Username</label> <label for="floatingInput">Username</label>
<RRButton @click="setUser()" text="Confirm"></RRButton> <RRButton @click="setUser()" text="Confirm"></RRButton>
</div> </div>
@ -19,26 +19,26 @@ export default {
data() { data() {
return { return {
username: '', username: '',
user: null,
} }
}, },
emits: ['userChange'], emits: ['userChange'],
methods: { methods: {
async setUser() { async setUser() {
if (this.username === '') return;
let user; let user;
user = await User.getByName(this.username); user = await User.getByName(this.username);
if (user.errors) { if(user.errors){
user = await User.create(this.username); user = await User.create(this.username);
} }
if (user.errors) { if(user.errors){
console.error("Something when wrong when logging in, please contact admin!") console.error("Something when wrong when logging in, please contact admin!")
return; return;
} }
if (user) { if (user) {
this.user = user; this.$emit('userChange', user);
this.$emit('userChange', this.user);
} }
}, },
} }
@ -48,6 +48,18 @@ export default {
<style scoped> <style scoped>
input { input {
border: 3px solid black; border: 3px solid black;
border-radius: 0;
background-color: beige; background-color: beige;
margin-bottom: 5px;
}
input:focus {
background: beige;
border-color: rgba(184,134,11, 0.8);
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075) inset, 0 0 8px rgba(184,134,11, 0.6);
}
label {
margin-left: 10px;
} }
</style> </style>