diff --git a/Games/Multiplayer_Shooting_Game/.gitignore b/Games/Multiplayer_Shooting_Game/.gitignore new file mode 100644 index 0000000000..07bc295a77 --- /dev/null +++ b/Games/Multiplayer_Shooting_Game/.gitignore @@ -0,0 +1,2 @@ +.ds_store +node_modules \ No newline at end of file diff --git a/Games/Multiplayer_Shooting_Game/.prettierrc b/Games/Multiplayer_Shooting_Game/.prettierrc new file mode 100644 index 0000000000..553d3bfbd4 --- /dev/null +++ b/Games/Multiplayer_Shooting_Game/.prettierrc @@ -0,0 +1,13 @@ +{ + "singleQuote": true, + "semi": false, + "trailingComma": "none", + "overrides": [ + { + "files": "*.html", + "options": { + "parser": "html" + } + } + ] +} \ No newline at end of file diff --git a/Games/Multiplayer_Shooting_Game/backend.js b/Games/Multiplayer_Shooting_Game/backend.js new file mode 100644 index 0000000000..6b944c62f3 --- /dev/null +++ b/Games/Multiplayer_Shooting_Game/backend.js @@ -0,0 +1,169 @@ +const express = require('express') +const app = express() + +// socket.io setup +const http = require('http') +const server = http.createServer(app) +const { Server } = require('socket.io') +const io = new Server(server, { pingInterval: 2000, pingTimeout: 5000 }) + +const port = 3000 + +app.use(express.static('public')) + +app.get('/', (req, res) => { + res.sendFile(__dirname + '/index.html') +}) + +const backEndPlayers = {} +const backEndProjectiles = {} + +const SPEED = 5 +const RADIUS = 10 +const PROJECTILE_RADIUS = 5 +let projectileId = 0 + +io.on('connection', (socket) => { + console.log('a user connected') + + io.emit('updatePlayers', backEndPlayers) + + socket.on('shoot', ({ x, y, angle }) => { + projectileId++ + + const velocity = { + x: Math.cos(angle) * 5, + y: Math.sin(angle) * 5 + } + + backEndProjectiles[projectileId] = { + x, + y, + velocity, + playerId: socket.id + } + + console.log(backEndProjectiles) + }) + + socket.on('initGame', ({ username, width, height }) => { + backEndPlayers[socket.id] = { + x: 1024 * Math.random(), + y: 576 * Math.random(), + color: `hsl(${360 * Math.random()}, 100%, 50%)`, + sequenceNumber: 0, + score: 0, + username + } + + // where we init our canvas + backEndPlayers[socket.id].canvas = { + width, + height + } + + backEndPlayers[socket.id].radius = RADIUS + }) + + socket.on('disconnect', (reason) => { + console.log(reason) + delete backEndPlayers[socket.id] + io.emit('updatePlayers', backEndPlayers) + }) + + socket.on('keydown', ({ keycode, sequenceNumber }) => { + const backEndPlayer = backEndPlayers[socket.id] + + if (!backEndPlayers[socket.id]) return + + backEndPlayers[socket.id].sequenceNumber = sequenceNumber + switch (keycode) { + case 'KeyW': + backEndPlayers[socket.id].y -= SPEED + break + + case 'KeyA': + backEndPlayers[socket.id].x -= SPEED + break + + case 'KeyS': + backEndPlayers[socket.id].y += SPEED + break + + case 'KeyD': + backEndPlayers[socket.id].x += SPEED + break + } + + const playerSides = { + left: backEndPlayer.x - backEndPlayer.radius, + right: backEndPlayer.x + backEndPlayer.radius, + top: backEndPlayer.y - backEndPlayer.radius, + bottom: backEndPlayer.y + backEndPlayer.radius + } + + if (playerSides.left < 0) backEndPlayers[socket.id].x = backEndPlayer.radius + + if (playerSides.right > 1024) + backEndPlayers[socket.id].x = 1024 - backEndPlayer.radius + + if (playerSides.top < 0) backEndPlayers[socket.id].y = backEndPlayer.radius + + if (playerSides.bottom > 576) + backEndPlayers[socket.id].y = 576 - backEndPlayer.radius + }) +}) + +// backend ticker +setInterval(() => { + // update projectile positions + for (const id in backEndProjectiles) { + backEndProjectiles[id].x += backEndProjectiles[id].velocity.x + backEndProjectiles[id].y += backEndProjectiles[id].velocity.y + + const PROJECTILE_RADIUS = 5 + if ( + backEndProjectiles[id].x - PROJECTILE_RADIUS >= + backEndPlayers[backEndProjectiles[id].playerId]?.canvas?.width || + backEndProjectiles[id].x + PROJECTILE_RADIUS <= 0 || + backEndProjectiles[id].y - PROJECTILE_RADIUS >= + backEndPlayers[backEndProjectiles[id].playerId]?.canvas?.height || + backEndProjectiles[id].y + PROJECTILE_RADIUS <= 0 + ) { + delete backEndProjectiles[id] + continue + } + + for (const playerId in backEndPlayers) { + const backEndPlayer = backEndPlayers[playerId] + + const DISTANCE = Math.hypot( + backEndProjectiles[id].x - backEndPlayer.x, + backEndProjectiles[id].y - backEndPlayer.y + ) + + // collision detection + if ( + DISTANCE < PROJECTILE_RADIUS + backEndPlayer.radius && + backEndProjectiles[id].playerId !== playerId + ) { + if (backEndPlayers[backEndProjectiles[id].playerId]) + backEndPlayers[backEndProjectiles[id].playerId].score++ + + console.log(backEndPlayers[backEndProjectiles[id].playerId]) + delete backEndProjectiles[id] + delete backEndPlayers[playerId] + break + } + } + } + + io.emit('updateProjectiles', backEndProjectiles) + io.emit('updatePlayers', backEndPlayers) +}, 15) + +server.listen(port, () => { + console.log(`Example app listening on port ${port}`) +}) + +console.log('server did load') diff --git a/Games/Multiplayer_Shooting_Game/package-lock.json b/Games/Multiplayer_Shooting_Game/package-lock.json new file mode 100644 index 0000000000..b1d264b51b --- /dev/null +++ b/Games/Multiplayer_Shooting_Game/package-lock.json @@ -0,0 +1,795 @@ +{ + "name": "multiplayer-game-starter-main", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "express": "4.18.2", + "socket.io": "4.6.1" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" + }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + }, + "node_modules/@types/cors": { + "version": "2.8.13", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", + "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "18.16.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.1.tgz", + "integrity": "sha512-DZxSZWXxFfOlx7k7Rv4LAyiMroaxa3Ly/7OOzZO8cBNho0YzAi4qlbrx8W27JGqG57IgR/6J7r+nOJWw6kcvZA==" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/engine.io": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.1.tgz", + "integrity": "sha512-JFYQurD/nbsA5BSPmbaOSLa3tSVj8L6o4srSwXXY3NqE+gGUNmmPTbhn8tjzcCtSqhFgIeqef81ngny8JM25hw==", + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.11.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", + "integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/socket.io": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.1.tgz", + "integrity": "sha512-KMcaAi4l/8+xEjkRICl6ak8ySoxsYG+gG6/XfRCPJPQ/haCRIJBTL4wIl8YCsmtaBovcAXGLOShyVWQ/FG8GZA==", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "debug": "~4.3.2", + "engine.io": "~6.4.1", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", + "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", + "dependencies": { + "ws": "~8.11.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.2.tgz", + "integrity": "sha512-DJtziuKypFkMMHCm2uIshOYC7QaylbtzQwiMYDuCKy3OPkjLzu4B2vAhTlqipRHHzrI0NJeBAizTK7X+6m1jVw==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + } +} diff --git a/Games/Multiplayer_Shooting_Game/package.json b/Games/Multiplayer_Shooting_Game/package.json new file mode 100644 index 0000000000..ad6c4119b3 --- /dev/null +++ b/Games/Multiplayer_Shooting_Game/package.json @@ -0,0 +1,6 @@ +{ + "dependencies": { + "express": "4.18.2", + "socket.io": "4.6.1" + } +} diff --git a/Games/Multiplayer_Shooting_Game/public/img/webb-dark.png b/Games/Multiplayer_Shooting_Game/public/img/webb-dark.png new file mode 100644 index 0000000000..c175914fcc Binary files /dev/null and b/Games/Multiplayer_Shooting_Game/public/img/webb-dark.png differ diff --git a/Games/Multiplayer_Shooting_Game/public/index.html b/Games/Multiplayer_Shooting_Game/public/index.html new file mode 100644 index 0000000000..1ec66cb74d --- /dev/null +++ b/Games/Multiplayer_Shooting_Game/public/index.html @@ -0,0 +1,106 @@ + + + + + +
+
+
Leaderboard
+
+
+ + + +
+
+
+ +
+
+ +
+
+
+ +
+ + + + + + + + + + + diff --git a/Games/Multiplayer_Shooting_Game/public/js/classes/Enemy.js b/Games/Multiplayer_Shooting_Game/public/js/classes/Enemy.js new file mode 100644 index 0000000000..a17c0e7ade --- /dev/null +++ b/Games/Multiplayer_Shooting_Game/public/js/classes/Enemy.js @@ -0,0 +1,22 @@ +class Enemy { + constructor(x, y, radius, color, velocity) { + this.x = x + this.y = y + this.radius = radius + this.color = color + this.velocity = velocity + } + + draw() { + c.beginPath() + c.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false) + c.fillStyle = this.color + c.fill() + } + + update() { + this.draw() + this.x = this.x + this.velocity.x + this.y = this.y + this.velocity.y + } +} diff --git a/Games/Multiplayer_Shooting_Game/public/js/classes/Particle.js b/Games/Multiplayer_Shooting_Game/public/js/classes/Particle.js new file mode 100644 index 0000000000..05df89b8f9 --- /dev/null +++ b/Games/Multiplayer_Shooting_Game/public/js/classes/Particle.js @@ -0,0 +1,30 @@ +const friction = 0.99 +class Particle { + constructor(x, y, radius, color, velocity) { + this.x = x + this.y = y + this.radius = radius + this.color = color + this.velocity = velocity + this.alpha = 1 + } + + draw() { + c.save() + c.globalAlpha = this.alpha + c.beginPath() + c.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false) + c.fillStyle = this.color + c.fill() + c.restore() + } + + update() { + this.draw() + this.velocity.x *= friction + this.velocity.y *= friction + this.x = this.x + this.velocity.x + this.y = this.y + this.velocity.y + this.alpha -= 0.01 + } +} diff --git a/Games/Multiplayer_Shooting_Game/public/js/classes/Player.js b/Games/Multiplayer_Shooting_Game/public/js/classes/Player.js new file mode 100644 index 0000000000..43fc4236aa --- /dev/null +++ b/Games/Multiplayer_Shooting_Game/public/js/classes/Player.js @@ -0,0 +1,23 @@ +class Player { + constructor({ x, y, radius, color, username }) { + this.x = x + this.y = y + this.radius = radius + this.color = color + this.username = username + } + + draw() { + c.font = '12px sans-serif' + c.fillStyle = 'white' + c.fillText(this.username, this.x - 10, this.y + 20) + c.save() + c.shadowColor = this.color + c.shadowBlur = 20 + c.beginPath() + c.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false) + c.fillStyle = this.color + c.fill() + c.restore() + } +} diff --git a/Games/Multiplayer_Shooting_Game/public/js/classes/Projectile.js b/Games/Multiplayer_Shooting_Game/public/js/classes/Projectile.js new file mode 100644 index 0000000000..f65e3f00de --- /dev/null +++ b/Games/Multiplayer_Shooting_Game/public/js/classes/Projectile.js @@ -0,0 +1,26 @@ +class Projectile { + constructor({ x, y, radius, color = 'white', velocity }) { + this.x = x + this.y = y + this.radius = radius + this.color = color + this.velocity = velocity + } + + draw() { + c.save() + c.shadowColor = this.color + c.shadowBlur = 20 + c.beginPath() + c.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false) + c.fillStyle = this.color + c.fill() + c.restore() + } + + update() { + this.draw() + this.x = this.x + this.velocity.x + this.y = this.y + this.velocity.y + } +} diff --git a/Games/Multiplayer_Shooting_Game/public/js/eventListeners.js b/Games/Multiplayer_Shooting_Game/public/js/eventListeners.js new file mode 100644 index 0000000000..bb1b57920b --- /dev/null +++ b/Games/Multiplayer_Shooting_Game/public/js/eventListeners.js @@ -0,0 +1,35 @@ +addEventListener('click', (event) => { + const canvas = document.querySelector('canvas') + const { top, left } = canvas.getBoundingClientRect() + const playerPosition = { + x: frontEndPlayers[socket.id].x, + y: frontEndPlayers[socket.id].y + } + + const angle = Math.atan2( + event.clientY - top - playerPosition.y, + event.clientX - left - playerPosition.x + ) + + // const velocity = { + // x: Math.cos(angle) * 5, + // y: Math.sin(angle) * 5 + // } + + socket.emit('shoot', { + x: playerPosition.x, + y: playerPosition.y, + angle + }) + // frontEndProjectiles.push( + // new Projectile({ + // x: playerPosition.x, + // y: playerPosition.y, + // radius: 5, + // color: 'white', + // velocity + // }) + // ) + + console.log(frontEndProjectiles) +}) diff --git a/Games/Multiplayer_Shooting_Game/public/js/frontend.js b/Games/Multiplayer_Shooting_Game/public/js/frontend.js new file mode 100644 index 0000000000..d3140bf7af --- /dev/null +++ b/Games/Multiplayer_Shooting_Game/public/js/frontend.js @@ -0,0 +1,262 @@ +const canvas = document.querySelector('canvas') +const c = canvas.getContext('2d') + +const socket = io() + +const scoreEl = document.querySelector('#scoreEl') + +const devicePixelRatio = window.devicePixelRatio || 1 + +canvas.width = 1024 * devicePixelRatio +canvas.height = 576 * devicePixelRatio + +c.scale(devicePixelRatio, devicePixelRatio) + +const x = canvas.width / 2 +const y = canvas.height / 2 + +const frontEndPlayers = {} +const frontEndProjectiles = {} + +socket.on('updateProjectiles', (backEndProjectiles) => { + for (const id in backEndProjectiles) { + const backEndProjectile = backEndProjectiles[id] + + if (!frontEndProjectiles[id]) { + frontEndProjectiles[id] = new Projectile({ + x: backEndProjectile.x, + y: backEndProjectile.y, + radius: 5, + color: frontEndPlayers[backEndProjectile.playerId]?.color, + velocity: backEndProjectile.velocity + }) + } else { + frontEndProjectiles[id].x += backEndProjectiles[id].velocity.x + frontEndProjectiles[id].y += backEndProjectiles[id].velocity.y + } + } + + for (const frontEndProjectileId in frontEndProjectiles) { + if (!backEndProjectiles[frontEndProjectileId]) { + delete frontEndProjectiles[frontEndProjectileId] + } + } +}) + +socket.on('updatePlayers', (backEndPlayers) => { + for (const id in backEndPlayers) { + const backEndPlayer = backEndPlayers[id] + + if (!frontEndPlayers[id]) { + frontEndPlayers[id] = new Player({ + x: backEndPlayer.x, + y: backEndPlayer.y, + radius: 10, + color: backEndPlayer.color, + username: backEndPlayer.username + }) + + document.querySelector( + '#playerLabels' + ).innerHTML += `
${backEndPlayer.username}: ${backEndPlayer.score}
` + } else { + document.querySelector( + `div[data-id="${id}"]` + ).innerHTML = `${backEndPlayer.username}: ${backEndPlayer.score}` + + document + .querySelector(`div[data-id="${id}"]`) + .setAttribute('data-score', backEndPlayer.score) + + // sorts the players divs + const parentDiv = document.querySelector('#playerLabels') + const childDivs = Array.from(parentDiv.querySelectorAll('div')) + + childDivs.sort((a, b) => { + const scoreA = Number(a.getAttribute('data-score')) + const scoreB = Number(b.getAttribute('data-score')) + + return scoreB - scoreA + }) + + // removes old elements + childDivs.forEach((div) => { + parentDiv.removeChild(div) + }) + + // adds sorted elements + childDivs.forEach((div) => { + parentDiv.appendChild(div) + }) + + frontEndPlayers[id].target = { + x: backEndPlayer.x, + y: backEndPlayer.y + } + + if (id === socket.id) { + const lastBackendInputIndex = playerInputs.findIndex((input) => { + return backEndPlayer.sequenceNumber === input.sequenceNumber + }) + + if (lastBackendInputIndex > -1) + playerInputs.splice(0, lastBackendInputIndex + 1) + + playerInputs.forEach((input) => { + frontEndPlayers[id].target.x += input.dx + frontEndPlayers[id].target.y += input.dy + }) + } + } + } + + // this is where we delete frontend players + for (const id in frontEndPlayers) { + if (!backEndPlayers[id]) { + const divToDelete = document.querySelector(`div[data-id="${id}"]`) + divToDelete.parentNode.removeChild(divToDelete) + + if (id === socket.id) { + document.querySelector('#usernameForm').style.display = 'block' + } + + delete frontEndPlayers[id] + } + } +}) + +let animationId +function animate() { + animationId = requestAnimationFrame(animate) + // c.fillStyle = 'rgba(0, 0, 0, 0.1)' + c.clearRect(0, 0, canvas.width, canvas.height) + + for (const id in frontEndPlayers) { + const frontEndPlayer = frontEndPlayers[id] + + // linear interpolation + if (frontEndPlayer.target) { + frontEndPlayers[id].x += + (frontEndPlayers[id].target.x - frontEndPlayers[id].x) * 0.5 + frontEndPlayers[id].y += + (frontEndPlayers[id].target.y - frontEndPlayers[id].y) * 0.5 + } + + frontEndPlayer.draw() + } + + for (const id in frontEndProjectiles) { + const frontEndProjectile = frontEndProjectiles[id] + frontEndProjectile.draw() + } + + // for (let i = frontEndProjectiles.length - 1; i >= 0; i--) { + // const frontEndProjectile = frontEndProjectiles[i] + // frontEndProjectile.update() + // } +} + +animate() + +const keys = { + w: { + pressed: false + }, + a: { + pressed: false + }, + s: { + pressed: false + }, + d: { + pressed: false + } +} + +const SPEED = 5 +const playerInputs = [] +let sequenceNumber = 0 +setInterval(() => { + if (keys.w.pressed) { + sequenceNumber++ + playerInputs.push({ sequenceNumber, dx: 0, dy: -SPEED }) + // frontEndPlayers[socket.id].y -= SPEED + socket.emit('keydown', { keycode: 'KeyW', sequenceNumber }) + } + + if (keys.a.pressed) { + sequenceNumber++ + playerInputs.push({ sequenceNumber, dx: -SPEED, dy: 0 }) + // frontEndPlayers[socket.id].x -= SPEED + socket.emit('keydown', { keycode: 'KeyA', sequenceNumber }) + } + + if (keys.s.pressed) { + sequenceNumber++ + playerInputs.push({ sequenceNumber, dx: 0, dy: SPEED }) + // frontEndPlayers[socket.id].y += SPEED + socket.emit('keydown', { keycode: 'KeyS', sequenceNumber }) + } + + if (keys.d.pressed) { + sequenceNumber++ + playerInputs.push({ sequenceNumber, dx: SPEED, dy: 0 }) + // frontEndPlayers[socket.id].x += SPEED + socket.emit('keydown', { keycode: 'KeyD', sequenceNumber }) + } +}, 15) + +window.addEventListener('keydown', (event) => { + if (!frontEndPlayers[socket.id]) return + + switch (event.code) { + case 'KeyW': + keys.w.pressed = true + break + + case 'KeyA': + keys.a.pressed = true + break + + case 'KeyS': + keys.s.pressed = true + break + + case 'KeyD': + keys.d.pressed = true + break + } +}) + +window.addEventListener('keyup', (event) => { + if (!frontEndPlayers[socket.id]) return + + switch (event.code) { + case 'KeyW': + keys.w.pressed = false + break + + case 'KeyA': + keys.a.pressed = false + break + + case 'KeyS': + keys.s.pressed = false + break + + case 'KeyD': + keys.d.pressed = false + break + } +}) + +document.querySelector('#usernameForm').addEventListener('submit', (event) => { + event.preventDefault() + document.querySelector('#usernameForm').style.display = 'none' + socket.emit('initGame', { + width: canvas.width, + height: canvas.height, + devicePixelRatio, + username: document.querySelector('#usernameInput').value + }) +}) diff --git a/Games/Multiplayer_Shooting_Game/readme.md b/Games/Multiplayer_Shooting_Game/readme.md new file mode 100644 index 0000000000..b040e0e575 --- /dev/null +++ b/Games/Multiplayer_Shooting_Game/readme.md @@ -0,0 +1,3 @@ +# Multiplayer online shooting game + +[Portfolio](https://vaibhavkesarwani.vercel.app) diff --git a/README.md b/README.md index 6c2158c946..a7918e1f5d 100644 --- a/README.md +++ b/README.md @@ -1662,7 +1662,7 @@ This repository also provides one such platforms where contributers come over an | [Airhockey_game](https://github.com/kunjgit/GameZone/tree/main/Games/Airhockey_game)| |[King_Sword_Runner](https://github.com/kunjgit/GameZone/tree/main/Games/King_Sword_Runner)| - +|[Multiplayer_Shooting_Game](https://github.com/kunjgit/GameZone/tree/main/Games/Multiplayer_Shooting_Game)| |[Smash-Monty-Mole](https://github.com/kunjgit/GameZone/tree/main/Smash-Monty-mole-master-main)| | [Hole_And_Mole_Game](https://github.com/kunjgit/GameZone/tree/main/Games/Hole_And_Mole_Game)| |[Animal_Name_Guessing](https://github.com/kunjgit/GameZone/tree/main/Games/Animal_Name_Guessing)| diff --git a/assets/images/Multiplayer_Shooting_Game.jpeg b/assets/images/Multiplayer_Shooting_Game.jpeg new file mode 100644 index 0000000000..9ec891ee6c Binary files /dev/null and b/assets/images/Multiplayer_Shooting_Game.jpeg differ diff --git a/assets/js/gamesData.json b/assets/js/gamesData.json index 99cae8d17f..0f7a63fc6b 100644 --- a/assets/js/gamesData.json +++ b/assets/js/gamesData.json @@ -3394,5 +3394,10 @@ "gameTitle" : "King_Sword_Runner", "gameUrl": "King_Sword_Runner", "thumbnailUrl": "King_Sword_Runner.png" + }, + "637":{ + "gameTitle" : "Multiplayer_Shooting_Game", + "gameUrl": "Multiplayer_Shooting_Game", + "thumbnailUrl": "Multiplayer_Shooting_Game.jpeg" } }