diff --git a/.env.example b/.env.example index 821e903..f46c8c7 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,6 @@ POSTGRES_PORT=5432 EXPRESS_PORT=3000 +FRONTEND_PORT=8080 POSTGRES_USER=postgres POSTGRES_PASSWORD=postgres diff --git a/README.md b/README.md index 71ff3b6..82d8404 100644 --- a/README.md +++ b/README.md @@ -1,163 +1,30 @@ # RaspberryRocketeer -## Class Diagram of all classes +## How to run -```mermaid -classDiagram -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 +### Copy .env +First you need to copy the `.env.example`. +```shell +cp .env.example .env ``` + +Note: It is recommended to change the values for the database user. + +### 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` diff --git a/backend/api/src/gameRoute.ts b/backend/api/src/gameRoute.ts index 2ac9175..0de3e2c 100644 --- a/backend/api/src/gameRoute.ts +++ b/backend/api/src/gameRoute.ts @@ -9,6 +9,9 @@ export const gameRoute = express.Router() gameRoute.use(express.json()) +/** + * Test + */ gameRoute.post( '/add', body('playtime') @@ -18,6 +21,8 @@ gameRoute.post( body('userId') .isInt({min: 1}) .custom(userWithIdExists), + body('score') + .isInt({min: 0}), /** * After processing the errors of express-validator, inserts the game into the DB * @param req diff --git a/backend/api/src/leaderboardRoute.ts b/backend/api/src/leaderboardRoute.ts index 0c5ce51..699180c 100644 --- a/backend/api/src/leaderboardRoute.ts +++ b/backend/api/src/leaderboardRoute.ts @@ -27,7 +27,7 @@ leaderboardRoute.get('/highscore', //endregion const highscoreLeaderboardRepo: HighscoreLeaderboardRepository = new HighscoreLeaderboardPgPromiseRepository; let highscoreLeaderboard: HighscoreLeaderboard; - if (req.query.pagination) { + if (req.query.pagination == true) { const entriesPerPage = req.query.entriesPerPage; const page = req.query.page; highscoreLeaderboard = await highscoreLeaderboardRepo.getPage(entriesPerPage, page); @@ -53,6 +53,7 @@ leaderboardRoute.get('/totalplaytime', */ async (req, res) => { try { + console.log(req.query) //region validate parameters const errors = validationResult(req); if (!errors.isEmpty()) { @@ -61,7 +62,8 @@ leaderboardRoute.get('/totalplaytime', //endregion const timeLeaderboardRepo: TimeLeaderboardRepository = new TimeLeaderboardPgPromiseRepository; let timeLeaderboard: TimeLeaderboard; - if (req.query.pagination) { + console.log(req.query) + if (req.query.pagination == true) { const entriesPerPage = req.query.entriesPerPage; const page = req.query.page; timeLeaderboard = await timeLeaderboardRepo.getPage(entriesPerPage, page); diff --git a/backend/api/src/userRoute.ts b/backend/api/src/userRoute.ts index e87056f..4061969 100644 --- a/backend/api/src/userRoute.ts +++ b/backend/api/src/userRoute.ts @@ -84,3 +84,30 @@ userRoute.get('/:userId/scores', } } ) + +userRoute.get('/:name', + param('name') + .isString() + .isLength({min: 3, max: 32}) + .matches(USERNAME_VALIDATION_REGEX), + + async (req, res) => { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ errors: errors.array() }); + } + + const name: string = req.params.name; + console.log(name) + + try { + // get & return data + const userRepo: UserPgPromiseRepository = new UserPgPromiseRepository(); + const user = await userRepo.getByName(name); + res.json(user); + } catch (error) { + // handle errors + console.log(error) + res.status(500).json({ errors: [{msg: "Internal server error"}]}) + } + }) diff --git a/backend/db/initScripts/loadMockData.sql b/backend/db/initScripts/loadMockData.sql deleted file mode 100644 index 7735cc9..0000000 --- a/backend/db/initScripts/loadMockData.sql +++ /dev/null @@ -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'); \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index f21adcc..2672685 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,23 +1,32 @@ -version: '3.1' - -services: - db: - build: backend/db - container_name: postgres-db - environment: - POSTGRES_DB: ${POSTGRES_DB} - POSTGRES_USER: ${POSTGRES_USER} - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} - ports: - - "${POSTGRES_PORT}:5432" - volumes: - - ./backend/pgdata:/var/lib/postgresql/data - - api: - build: backend/api - depends_on: - - db - container_name: express-api - ports: - - "${EXPRESS_PORT}:3000" - +version: '3.1' + +services: + db: + build: backend/db + container_name: postgres-db + environment: + POSTGRES_DB: ${POSTGRES_DB} + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + ports: + - "${POSTGRES_PORT}:5432" + volumes: + - ./backend/pgdata:/var/lib/postgresql/data + + api: + build: backend/api + depends_on: + - db + container_name: express-api + ports: + - "${EXPRESS_PORT}:3000" + + vue: + build: frontend + container_name: frontend + ports: + - "${FRONTEND_PORT}:8080" + + + volumes: + - ./frontend:/app diff --git a/frontend/.dockerignore b/frontend/.dockerignore new file mode 100644 index 0000000..3298622 --- /dev/null +++ b/frontend/.dockerignore @@ -0,0 +1,3 @@ +node_modules +.gitignore +README.md diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..e8524c9 --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,11 @@ +FROM node:18 + +COPY . /app +WORKDIR /app + +RUN npm install + +EXPOSE 8080 + +ENTRYPOINT ["npm", "run", "serve"] + diff --git a/frontend/package-lock.json b/frontend/package-lock.json index e0d7807..52c94eb 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,9 +8,7 @@ "name": "raspberryrocketeer", "version": "0.1.0", "dependencies": { - "@types/p5": "^1.4.3", "bootstrap": "^5.2.3", - "p5": "^1.5.0", "vue": "^3.2.13" }, "devDependencies": { @@ -831,11 +829,6 @@ "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", "dev": true }, - "node_modules/@types/p5": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@types/p5/-/p5-1.4.3.tgz", - "integrity": "sha512-UjgBi1/VU0Dz1/JxzRZrDKlC54rMELWW+6oHx1pRKzKkVgwzvP+dEjZ1JbeMjK12VJMUjDx4lhGUmnUL2jBASQ==" - }, "node_modules/@types/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmmirror.com/@types/parse-json/-/parse-json-4.0.0.tgz", @@ -6194,11 +6187,6 @@ "node": ">=6" } }, - "node_modules/p5": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/p5/-/p5-1.5.0.tgz", - "integrity": "sha512-zZFMVUmGkXe2G5H6Sw7xsVhgdxMyEN/6SZnZqYdQ51513kTqPslLnukkwTbGf8YtW0RetTU0FTjYQMXnFD7KnQ==" - }, "node_modules/param-case": { "version": "3.0.4", "resolved": "https://registry.npmmirror.com/param-case/-/param-case-3.0.4.tgz", @@ -9881,11 +9869,6 @@ "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", "dev": true }, - "@types/p5": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@types/p5/-/p5-1.4.3.tgz", - "integrity": "sha512-UjgBi1/VU0Dz1/JxzRZrDKlC54rMELWW+6oHx1pRKzKkVgwzvP+dEjZ1JbeMjK12VJMUjDx4lhGUmnUL2jBASQ==" - }, "@types/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmmirror.com/@types/parse-json/-/parse-json-4.0.0.tgz", @@ -14205,11 +14188,6 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, - "p5": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/p5/-/p5-1.5.0.tgz", - "integrity": "sha512-zZFMVUmGkXe2G5H6Sw7xsVhgdxMyEN/6SZnZqYdQ51513kTqPslLnukkwTbGf8YtW0RetTU0FTjYQMXnFD7KnQ==" - }, "param-case": { "version": "3.0.4", "resolved": "https://registry.npmmirror.com/param-case/-/param-case-3.0.4.tgz", diff --git a/frontend/package.json b/frontend/package.json index 5a8b651..a11bdcd 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -9,14 +9,12 @@ "lint": "vue-cli-service lint" }, "dependencies": { - "@types/p5": "^1.4.3", "bootstrap": "^5.2.3", - "p5": "^1.5.0", - "vue": "^3.2.13" + "vue": "^3.2.13", + "@vue/cli-service": "~5.0.0" }, "devDependencies": { "@vue/cli-plugin-typescript": "~5.0.0", - "@vue/cli-service": "~5.0.0", "typescript": "~4.5.5" } } diff --git a/frontend/public/game.js b/frontend/public/game.js new file mode 100644 index 0000000..3b51b91 --- /dev/null +++ b/frontend/public/game.js @@ -0,0 +1,519 @@ +var __extends = (this && this.__extends) || (function(){ + var extendStatics = function(d, b){ + extendStatics = Object.setPrototypeOf || + ({__proto__: []} instanceof Array && function(d, b){ + d.__proto__ = b; + }) || + function(d, b){ + for(var p in b) if(Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; + }; + return extendStatics(d, b); + }; + return function(d, b){ + if(typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + + function __(){ + this.constructor = d; + } + + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var PIPE_IMAGE_PATH = "resources/dell-pc-min-min-small.png"; +var BACKGROUND_IMAGE_PATH = "resources/htl-steyr-front.jpg"; +var RASPBERRY_IMAGE_PATH = "resources/raspberry-rocket.png"; +var FLOOR_IMAGE_PATH = "resources/table-min-min.png"; +var FONT_PATH = "resources/PressStart2P-Regular.ttf"; +var OBSTACLE_COUNT = 3; +var BOOST_KEYS = ["k", " "]; +var floorHeight; +var obstacleWidth; +var obstacleOffset; +var backgroundImage; +var pipeImage; +var floorImage; +var font; +var obstacles = []; +var raspberry; +var startTime; +var playTime; +var score = 0; +var paused; +var hasAlreadyScored = false; +var hasDied = true; +var ready = true; + +function preload(){ + font = loadFont(FONT_PATH); + backgroundImage = loadImage(BACKGROUND_IMAGE_PATH); + pipeImage = loadImage(PIPE_IMAGE_PATH); + floorImage = loadImage(FLOOR_IMAGE_PATH); +} + +function setup(){ + createCanvas(1085, 600); + floorHeight = height / 5; + setupObstacleConsts(); + setupFont(); + setupGame(); + var originalSetItem = localStorage.setItem; + localStorage.setItem = function(key, value){ + var event = new Event('itemInserted'); + + event.value = value; // Optional.. + event.key = key; // Optional.. + window.dispatchEvent(event); + originalSetItem.apply(this, arguments); + }; +} + +function setupObstacleConsts(){ + obstacleOffset = width / OBSTACLE_COUNT; + obstacleWidth = width / 22.727272727272727272; + Obstacle.distanceBetweenPipes = height / 2.5; + Obstacle.startX = width; +} + +function setupFont(){ + textSize(75); + textAlign(CENTER); + textFont(font); +} + +function setupGame(){ + paused = true; + raspberry = new Raspberry(RASPBERRY_IMAGE_PATH); + setupObstacles(); +} + +function setupObstacles(){ + obstacles = []; + instantiateObstacles(OBSTACLE_COUNT); + obstacles.forEach(function(obstacle){ + return obstacle.randomizeHeight(); + }); +} + +function instantiateObstacles(number){ + for(var i = 0; i < number; i++){ + obstacles.push(new Obstacle(new Position(width + obstacleOffset * i, 0), obstacleWidth, height, pipeImage)); + } +} + +function draw(){ + update(); + gameLoop(); + drawGame(); +} + +function drawGame(){ + drawScenery(); + drawEntities(); + displayScore(); +} + +function drawScenery(){ + background(backgroundImage); + drawFloor(); +} + +function drawFloor(){ + push(); + noFill(); + image(floorImage, 0, height - floorHeight, width, floorHeight); + rect(0, height - floorHeight, width, floorHeight); + pop(); +} + +function drawEntities(){ + raspberry.draw(); + drawObstacles(); +} + +function drawObstacles(){ + obstacles.forEach(function(obstacle){ + obstacle.draw(); + }); +} + +function gameLoop(){ + if(!paused){ + collisionCheck(obstacles[0]); + checkRaspberryScore(); + } +} + +function collisionCheck(o){ + if(o.collides(raspberry)){ + die(); + setupGame(); + } +} + +function die(){ + if(localStorage.getItem("frontend-ready") == "false") + return; + + ready = false; + hasDied = true; + playTime = Date.now() - startTime; + exportToLocalStorage(); + setTimeout(function(){ + return ready = true; + }, 1000); +} + +function exportToLocalStorage(){ + localStorage.setItem("game-playTime", String(playTime)); + localStorage.setItem("game-score", String(score)); + localStorage.setItem("game-isRunning", String(!hasDied)); +} + +function displayScore(){ + push(); + fill(195, 33, 34); + text(score, 0, height / 8, width, height); + pop(); +} + +function update(){ + if(!paused){ + raspberry.update(); + } + obstacles.forEach(function(obstacle){ + if(!paused){ + obstacle.update(); + checkObstacleReset(obstacle); + } + }); +} + +function checkObstacleReset(obstacle){ + if(obstacle.position.x < -obstacleWidth){ + obstacle.resetPosition(); + obstacles.shift(); + obstacles.push(obstacle); + hasAlreadyScored = false; + } +} + +function checkRaspberryScore(){ + if((obstacles[0].position.x + obstacles[0].width / 2) < (raspberry.position.x + raspberry.width / 2) + && !hasAlreadyScored){ + score += 1; + hasAlreadyScored = true; + } +} + +function resetScore(){ + if(!hasDied || localStorage.getItem("frontend-ready") == "false") + return; + + hasDied = false; + score = 0; + hasAlreadyScored = false; + startTime = Date.now(); + exportToLocalStorage(); +} + +function keyPressed(){ + if(!ready) + return; + if(BOOST_KEYS.includes(key.toLowerCase())){ + resetScore(); + raspberry.boost(); + } + if(key == "Escape"){ + paused = !paused; + } else if(paused){ + paused = false; + } +} + +var Entity = (function(){ + function Entity(position, width, height, fill){ + this.position = position; + this.width = width; + this.height = height; + this.fill = fill; + this._showHitbox = false; + } + + Object.defineProperty(Entity.prototype, "position", { + get: function(){ + return this._position; + }, + set: function(value){ + this._position = value; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(Entity.prototype, "width", { + get: function(){ + return this._width; + }, + set: function(value){ + this._width = value; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(Entity.prototype, "height", { + get: function(){ + return this._height; + }, + set: function(value){ + this._height = value; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(Entity.prototype, "showHitbox", { + get: function(){ + return this._showHitbox; + }, + set: function(value){ + this._showHitbox = value; + }, + enumerable: false, + configurable: true + }); + Entity.prototype.draw = function(){ + push(); + fill(this.fill); + rect(this.position.x, this.position.y, this.width, this.height); + pop(); + }; + return Entity; +}()); +var Obstacle = (function(_super){ + __extends(Obstacle, _super); + + function Obstacle(position, obstacleWidth, obstacleHeight, image){ + var _this = _super.call(this, position, obstacleWidth, obstacleHeight, 0) || this; + _this.speed = 3; + _this.padding = height / 6.6666666666666666; + _this.createPipes(position, obstacleHeight, obstacleWidth, image); + return _this; + } + + Object.defineProperty(Obstacle, "startX", { + set: function(value){ + this._startX = value; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(Obstacle, "distanceBetweenPipes", { + set: function(value){ + this._distanceBetweenPipes = value; + }, + enumerable: false, + configurable: true + }); + Obstacle.prototype.createPipes = function(position, obstacleHeight, obstacleWidth, pipeImage){ + this.pipeTop = new Pipe(position.x, obstacleWidth, obstacleHeight, pipeImage); + this.pipeBottom = new Pipe(position.x, obstacleWidth, obstacleHeight, pipeImage); + }; + Obstacle.prototype.resetPosition = function(){ + this.randomizeHeight(); + this.pipeBottom.position.x = Obstacle._startX; + this.pipeTop.position.x = Obstacle._startX; + }; + Obstacle.prototype.randomizeHeight = function(){ + this.pipeTop.height = this.randomRange(this.padding, height - this.padding - Obstacle._distanceBetweenPipes); + this.pipeBottom.position.y = this.pipeTop.height + Obstacle._distanceBetweenPipes; + this.pipeBottom.height = height - this.pipeTop.height - this.padding; + }; + Obstacle.prototype.randomRange = function(min, max){ + return Math.random() * (max - min) + min; + }; + Obstacle.prototype.update = function(){ + this.pipeTop.move(this.speed); + this.pipeBottom.move(this.speed); + this.position.x = this.pipeTop.position.x; + }; + Obstacle.prototype.draw = function(){ + this.pipeTop.draw(); + this.pipeBottom.draw(); + }; + Obstacle.prototype.collides = function(o){ + return this.pipeTop.collides(o) || this.pipeBottom.collides(o); + }; + return Obstacle; +}(Entity)); +var Pipe = (function(_super){ + __extends(Pipe, _super); + + function Pipe(positionX, width, height, image){ + var _this = _super.call(this, new Position(positionX, 0), width, height, 0) || this; + _this.image = image; + return _this; + } + + Pipe.prototype.update = function(){ + }; + Pipe.prototype.draw = function(){ + push(); + noFill(); + var imageAspectRatio = this.image.height / this.image.width; + var computedImageHeight = imageAspectRatio * this.width; + this.drawImage(computedImageHeight, imageAspectRatio); + rect(this.position.x, this.position.y, this.width, this.height); + pop(); + }; + Pipe.prototype.drawImage = function(computedImageHeight, imageAspectRatio){ + if(this.height > computedImageHeight){ + var maxImageYPos = Math.ceil(this.height / computedImageHeight) * computedImageHeight; + for(var imageYPosition = 0; imageYPosition < maxImageYPos; imageYPosition += computedImageHeight){ + if(imageYPosition + computedImageHeight >= maxImageYPos){ + this.cropLastImage(imageYPosition, computedImageHeight, imageAspectRatio); + break; + } + image(this.image, this.position.x, this.position.y + imageYPosition, this.width, computedImageHeight); + } + } else{ + image(this.image, this.position.x, this.position.y, this.width, this.height); + } + }; + Pipe.prototype.cropLastImage = function(imageYPosition, computedImageHeight, imageAspectRatio){ + var amountOfImages = Math.floor(imageYPosition / computedImageHeight); + var heightToEdge = this.height - (amountOfImages * computedImageHeight); + 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); + }; + Pipe.prototype.move = function(speed){ + this.position.x -= speed; + }; + Pipe.prototype.collides = function(o){ + return this.position.x < (o.position.x + o.width) && + (this.position.x + this.width) > o.position.x && + this.position.y < (o.position.y + o.height) && + (this.position.y + this.height) > o.position.y; + }; + return Pipe; +}(Entity)); +var Position = (function(){ + function Position(x, y){ + this._x = x; + this._y = y; + } + + Object.defineProperty(Position.prototype, "x", { + get: function(){ + return this._x; + }, + set: function(value){ + this._x = value; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(Position.prototype, "y", { + get: function(){ + return this._y; + }, + set: function(value){ + this._y = value; + }, + enumerable: false, + configurable: true + }); + return Position; +}()); +var Raspberry = (function(_super){ + __extends(Raspberry, _super); + + function Raspberry(image){ + var _this = this; + Raspberry.position = new Position(width / 6, height / 2); + Raspberry.height = height / 14.2857142857142857; + Raspberry.width = width / 11.1111111111111111; + _this = _super.call(this, Raspberry.position, Raspberry.width, Raspberry.height, Raspberry.FILL) || this; + _this.lift = -15; + _this.gravity = 0.45; + _this._velocity = 0; + Raspberry.bottomFloorOffset = (height / 5) - (height / 15 / 2); + _this.image = image; + return _this; + } + + Object.defineProperty(Raspberry.prototype, "velocity", { + get: function(){ + return this._velocity; + }, + set: function(value){ + this._velocity = (Math.abs(this.velocity) > Raspberry.maxVelocity) ? Raspberry.maxVelocity : value; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(Raspberry.prototype, "image", { + get: function(){ + return this._image; + }, + set: function(path){ + this._image = loadImage(path); + }, + enumerable: false, + configurable: true + }); + Raspberry.prototype.update = function(){ + this.applyGravity(); + this.forceBoundaries(); + }; + Raspberry.prototype.applyGravity = function(){ + this.velocity += this.gravity; + this.position.y += this.velocity; + }; + Raspberry.prototype.forceBoundaries = function(){ + this.boundaryTop(); + this.boundaryBottom(); + }; + Raspberry.prototype.boundaryTop = function(){ + if(this.position.y < 0){ + this.position.y = 0; + this.velocity = 0; + } + }; + Raspberry.prototype.boundaryBottom = function(){ + if(this.position.y + this.height + Raspberry.bottomFloorOffset > height){ + this.position.y = height - this.height - Raspberry.bottomFloorOffset; + this.velocity = 0; + } + }; + Raspberry.prototype.boost = function(){ + this.velocity += this.lift; + }; + Raspberry.prototype.draw = function(){ + push(); + noFill(); + this.setPose(); + this.drawObject(); + pop(); + }; + Raspberry.prototype.drawObject = function(){ + this.drawHitBox(); + this.drawRocket(); + }; + Raspberry.prototype.drawRocket = function(){ + image(this.image, 0, 0, this.width, this.height); + rect(0, 0, this.width, this.height); + }; + Raspberry.prototype.drawHitBox = function(){ + if(!this.showHitbox){ + noStroke(); + } + }; + Raspberry.prototype.setPose = function(){ + translate(this.position.x, this.position.y); + rotate((PI / 2) * (this.velocity / Raspberry.maxVelocity)); + }; + Raspberry.maxVelocity = 75; + Raspberry.FILL = 0; + return Raspberry; +}(Entity)); +//# sourceMappingURL=build.js.map diff --git a/frontend/public/index.html b/frontend/public/index.html index ead5f8f..fb663ea 100644 --- a/frontend/public/index.html +++ b/frontend/public/index.html @@ -10,8 +10,8 @@