├── plugins ├── feather.js └── README.md ├── static ├── sounds │ ├── game-lose.mp3 │ ├── game-tap.mp3 │ ├── game-win.mp3 │ ├── game-background.mp3 │ └── game-wrong-tap.mp3 └── img │ ├── favicon │ ├── favicon.ico │ ├── apple-icon.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon-96x96.png │ ├── ms-icon-144x144.png │ ├── ms-icon-150x150.png │ ├── ms-icon-310x310.png │ ├── ms-icon-70x70.png │ ├── apple-icon-57x57.png │ ├── apple-icon-60x60.png │ ├── apple-icon-72x72.png │ ├── apple-icon-76x76.png │ ├── android-icon-144x144.png │ ├── android-icon-192x192.png │ ├── android-icon-36x36.png │ ├── android-icon-48x48.png │ ├── android-icon-72x72.png │ ├── android-icon-96x96.png │ ├── apple-icon-114x114.png │ ├── apple-icon-120x120.png │ ├── apple-icon-144x144.png │ ├── apple-icon-152x152.png │ ├── apple-icon-180x180.png │ ├── apple-icon-precomposed.png │ ├── browserconfig.xml │ └── manifest.json │ └── vaccination-game-og.png ├── assets ├── images │ ├── game-background.png │ ├── game-face-imune.svg │ ├── game-face-sick.svg │ └── game-face-healthy.svg └── sass │ ├── global.sass │ └── basics │ ├── _body-element.sass │ ├── _typography.sass │ └── _bootstrap-override.scss ├── jsconfig.json ├── .editorconfig ├── pages ├── README.md └── index.vue ├── utils ├── Vec2.js └── GamePerson.js ├── middleware └── README.md ├── .travis.yml ├── store └── README.md ├── layouts └── default.vue ├── package.json ├── .gitignore ├── README.md ├── nuxt.config.js └── components └── Game.vue /plugins/feather.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import VueFeather from 'vue-feather'; 3 | 4 | Vue.use(VueFeather); 5 | -------------------------------------------------------------------------------- /static/sounds/game-lose.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claudiobonfati/vaccination-game/HEAD/static/sounds/game-lose.mp3 -------------------------------------------------------------------------------- /static/sounds/game-tap.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claudiobonfati/vaccination-game/HEAD/static/sounds/game-tap.mp3 -------------------------------------------------------------------------------- /static/sounds/game-win.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claudiobonfati/vaccination-game/HEAD/static/sounds/game-win.mp3 -------------------------------------------------------------------------------- /static/img/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claudiobonfati/vaccination-game/HEAD/static/img/favicon/favicon.ico -------------------------------------------------------------------------------- /assets/images/game-background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claudiobonfati/vaccination-game/HEAD/assets/images/game-background.png -------------------------------------------------------------------------------- /static/img/favicon/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claudiobonfati/vaccination-game/HEAD/static/img/favicon/apple-icon.png -------------------------------------------------------------------------------- /static/sounds/game-background.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claudiobonfati/vaccination-game/HEAD/static/sounds/game-background.mp3 -------------------------------------------------------------------------------- /static/sounds/game-wrong-tap.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claudiobonfati/vaccination-game/HEAD/static/sounds/game-wrong-tap.mp3 -------------------------------------------------------------------------------- /static/img/vaccination-game-og.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claudiobonfati/vaccination-game/HEAD/static/img/vaccination-game-og.png -------------------------------------------------------------------------------- /static/img/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claudiobonfati/vaccination-game/HEAD/static/img/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /static/img/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claudiobonfati/vaccination-game/HEAD/static/img/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /static/img/favicon/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claudiobonfati/vaccination-game/HEAD/static/img/favicon/favicon-96x96.png -------------------------------------------------------------------------------- /static/img/favicon/ms-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claudiobonfati/vaccination-game/HEAD/static/img/favicon/ms-icon-144x144.png -------------------------------------------------------------------------------- /static/img/favicon/ms-icon-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claudiobonfati/vaccination-game/HEAD/static/img/favicon/ms-icon-150x150.png -------------------------------------------------------------------------------- /static/img/favicon/ms-icon-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claudiobonfati/vaccination-game/HEAD/static/img/favicon/ms-icon-310x310.png -------------------------------------------------------------------------------- /static/img/favicon/ms-icon-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claudiobonfati/vaccination-game/HEAD/static/img/favicon/ms-icon-70x70.png -------------------------------------------------------------------------------- /static/img/favicon/apple-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claudiobonfati/vaccination-game/HEAD/static/img/favicon/apple-icon-57x57.png -------------------------------------------------------------------------------- /static/img/favicon/apple-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claudiobonfati/vaccination-game/HEAD/static/img/favicon/apple-icon-60x60.png -------------------------------------------------------------------------------- /static/img/favicon/apple-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claudiobonfati/vaccination-game/HEAD/static/img/favicon/apple-icon-72x72.png -------------------------------------------------------------------------------- /static/img/favicon/apple-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claudiobonfati/vaccination-game/HEAD/static/img/favicon/apple-icon-76x76.png -------------------------------------------------------------------------------- /static/img/favicon/android-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claudiobonfati/vaccination-game/HEAD/static/img/favicon/android-icon-144x144.png -------------------------------------------------------------------------------- /static/img/favicon/android-icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claudiobonfati/vaccination-game/HEAD/static/img/favicon/android-icon-192x192.png -------------------------------------------------------------------------------- /static/img/favicon/android-icon-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claudiobonfati/vaccination-game/HEAD/static/img/favicon/android-icon-36x36.png -------------------------------------------------------------------------------- /static/img/favicon/android-icon-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claudiobonfati/vaccination-game/HEAD/static/img/favicon/android-icon-48x48.png -------------------------------------------------------------------------------- /static/img/favicon/android-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claudiobonfati/vaccination-game/HEAD/static/img/favicon/android-icon-72x72.png -------------------------------------------------------------------------------- /static/img/favicon/android-icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claudiobonfati/vaccination-game/HEAD/static/img/favicon/android-icon-96x96.png -------------------------------------------------------------------------------- /static/img/favicon/apple-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claudiobonfati/vaccination-game/HEAD/static/img/favicon/apple-icon-114x114.png -------------------------------------------------------------------------------- /static/img/favicon/apple-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claudiobonfati/vaccination-game/HEAD/static/img/favicon/apple-icon-120x120.png -------------------------------------------------------------------------------- /static/img/favicon/apple-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claudiobonfati/vaccination-game/HEAD/static/img/favicon/apple-icon-144x144.png -------------------------------------------------------------------------------- /static/img/favicon/apple-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claudiobonfati/vaccination-game/HEAD/static/img/favicon/apple-icon-152x152.png -------------------------------------------------------------------------------- /static/img/favicon/apple-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claudiobonfati/vaccination-game/HEAD/static/img/favicon/apple-icon-180x180.png -------------------------------------------------------------------------------- /static/img/favicon/apple-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claudiobonfati/vaccination-game/HEAD/static/img/favicon/apple-icon-precomposed.png -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "~/*": ["./*"], 6 | "@/*": ["./*"], 7 | "~~/*": ["./*"], 8 | "@@/*": ["./*"] 9 | } 10 | }, 11 | "exclude": ["node_modules", ".nuxt", "dist"] 12 | } 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /static/img/favicon/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | #ffffff -------------------------------------------------------------------------------- /pages/README.md: -------------------------------------------------------------------------------- 1 | # PAGES 2 | 3 | This directory contains your Application Views and Routes. 4 | The framework reads all the `*.vue` files inside this directory and creates the router of your application. 5 | 6 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing). 7 | -------------------------------------------------------------------------------- /utils/Vec2.js: -------------------------------------------------------------------------------- 1 | export default class Vec2 { 2 | constructor(x, y) { 3 | this.x = x; 4 | this.y = y; 5 | } 6 | 7 | length () { 8 | return Math.sqrt((this.x * this.x) + (this.y*this.y)); 9 | } 10 | 11 | normalize () { 12 | let scale = this.length(); 13 | this.x /= scale; 14 | this.y /= scale; 15 | } 16 | } -------------------------------------------------------------------------------- /plugins/README.md: -------------------------------------------------------------------------------- 1 | # PLUGINS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains Javascript plugins that you want to run before mounting the root Vue.js application. 6 | 7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/plugins). 8 | -------------------------------------------------------------------------------- /middleware/README.md: -------------------------------------------------------------------------------- 1 | # MIDDLEWARE 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your application middleware. 6 | Middleware let you define custom functions that can be run before rendering either a page or a group of pages. 7 | 8 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing#middleware). 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 12 4 | cache: 5 | directories: 6 | - node_modules 7 | install: 8 | - npm install 9 | script: 10 | - npm run build 11 | - npm run generate 12 | deploy: 13 | provider: pages 14 | skip_cleanup: true 15 | github_token: $github_token 16 | local_dir: dist 17 | on: 18 | all_branches: true 19 | condition: $TRAVIS_BRANCH =~ ^(master|main)$ 20 | env: 21 | - BASE_URL=$base_url 22 | -------------------------------------------------------------------------------- /store/README.md: -------------------------------------------------------------------------------- 1 | # STORE 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your Vuex Store files. 6 | Vuex Store option is implemented in the Nuxt.js framework. 7 | 8 | Creating a file in this directory automatically activates the option in the framework. 9 | 10 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/vuex-store). 11 | -------------------------------------------------------------------------------- /layouts/default.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 16 | 17 | 24 | -------------------------------------------------------------------------------- /assets/sass/global.sass: -------------------------------------------------------------------------------- 1 | @charset "utf-8" 2 | 3 | // ================= Bootstrap Framework 4 | // ====== Import only what you need from Bootstrap 5 | @import "~bootstrap/scss/functions" 6 | @import "~bootstrap/scss/variables" 7 | @import "./basics/bootstrap-override" 8 | @import "~bootstrap/scss/mixins" 9 | @import "~bootstrap/scss/root" 10 | @import "~bootstrap/scss/reboot" 11 | @import "~bootstrap/scss/type" 12 | @import "~bootstrap/scss/grid" 13 | @import "~bootstrap/scss/utilities" 14 | 15 | // ================= Custom Styles 16 | @import "./basics/body-element" 17 | @import "./basics/typography" 18 | -------------------------------------------------------------------------------- /assets/sass/basics/_body-element.sass: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // BASICS > BODY ELEMENT 3 | // ========================================================================== 4 | 5 | body, 6 | html, 7 | #__nuxt, 8 | #__layout 9 | width: 100% 10 | height: 100% 11 | 12 | body 13 | color: #FFFFFF 14 | -webkit-font-smoothing: antialiased 15 | -webkit-text-size-adjust: 100% 16 | 17 | ::-webkit-scrollbar 18 | width: 0px 19 | background: transparent 20 | 21 | * 22 | outline: none 23 | -webkit-user-select: none 24 | -ms-user-select: none 25 | user-select: none 26 | 27 | &:focus, 28 | &:active 29 | outline: none 30 | -------------------------------------------------------------------------------- /assets/sass/basics/_typography.sass: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // BASICS > TYPOGRAPHY 3 | // ========================================================================== 4 | 5 | h1, h2, h3, h4, h5, h6 6 | font-weight: bold 7 | font-family: 'flama', BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif 8 | 9 | p 10 | margin: 0 0 20px 0 11 | font-size: 1.1rem 12 | font-family: 'flama', BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif 13 | 14 | em 15 | font-style: italic 16 | 17 | strong 18 | font-weight: bold 19 | 20 | hr 21 | border: solid #ddd 22 | border-width: 1px 0 0 23 | clear: both 24 | margin: 10px 0 30px 25 | height: 0 26 | -------------------------------------------------------------------------------- /static/img/favicon/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "App", 3 | "icons": [ 4 | { 5 | "src": "\/android-icon-36x36.png", 6 | "sizes": "36x36", 7 | "type": "image\/png", 8 | "density": "0.75" 9 | }, 10 | { 11 | "src": "\/android-icon-48x48.png", 12 | "sizes": "48x48", 13 | "type": "image\/png", 14 | "density": "1.0" 15 | }, 16 | { 17 | "src": "\/android-icon-72x72.png", 18 | "sizes": "72x72", 19 | "type": "image\/png", 20 | "density": "1.5" 21 | }, 22 | { 23 | "src": "\/android-icon-96x96.png", 24 | "sizes": "96x96", 25 | "type": "image\/png", 26 | "density": "2.0" 27 | }, 28 | { 29 | "src": "\/android-icon-144x144.png", 30 | "sizes": "144x144", 31 | "type": "image\/png", 32 | "density": "3.0" 33 | }, 34 | { 35 | "src": "\/android-icon-192x192.png", 36 | "sizes": "192x192", 37 | "type": "image\/png", 38 | "density": "4.0" 39 | } 40 | ] 41 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vaccination-game", 3 | "version": "1.0.0", 4 | "description": "A game that shows minimalistically how to eradicate a disease in a population. First, trap the disease as fast as possible, and then vaccinate all remaining people.", 5 | "author": "Claudio Bonfati", 6 | "private": true, 7 | "scripts": { 8 | "dev": "nuxt", 9 | "build": "nuxt build", 10 | "start": "nuxt start", 11 | "generate": "nuxt generate" 12 | }, 13 | "dependencies": { 14 | "bootstrap": "^4.5.2", 15 | "feather-icons": "^4.28.0", 16 | "gsap": "^3.5.1", 17 | "howler": "^2.2.0", 18 | "nuxt": "^2.14.4", 19 | "vue": "^2.6.13", 20 | "vue-feather": "^1.1.1", 21 | "vue2-github-corners": "^0.1.12" 22 | }, 23 | "devDependencies": { 24 | "@nuxtjs/style-resources": "^1.0.0", 25 | "fibers": "^4.0.3", 26 | "node-sass": "^4.14.1", 27 | "sass": "^1.26.10", 28 | "sass-loader": "^8.0.2" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /assets/images/game-face-imune.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /assets/images/game-face-sick.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /assets/images/game-face-healthy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Node template 3 | # Logs 4 | /logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | *.pid.lock 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # nyc test coverage 23 | .nyc_output 24 | 25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 26 | .grunt 27 | 28 | # Bower dependency directory (https://bower.io/) 29 | bower_components 30 | 31 | # node-waf configuration 32 | .lock-wscript 33 | 34 | # Compiled binary addons (https://nodejs.org/api/addons.html) 35 | build/Release 36 | 37 | # Dependency directories 38 | node_modules/ 39 | jspm_packages/ 40 | 41 | # TypeScript v1 declaration files 42 | typings/ 43 | 44 | # Optional npm cache directory 45 | .npm 46 | 47 | # Optional eslint cache 48 | .eslintcache 49 | 50 | # Optional REPL history 51 | .node_repl_history 52 | 53 | # Output of 'npm pack' 54 | *.tgz 55 | 56 | # Yarn Integrity file 57 | .yarn-integrity 58 | 59 | # dotenv environment variables file 60 | .env 61 | 62 | # parcel-bundler cache (https://parceljs.org/) 63 | .cache 64 | 65 | # next.js build output 66 | .next 67 | 68 | # nuxt.js build output 69 | .nuxt 70 | 71 | # Nuxt generate 72 | dist 73 | 74 | # vuepress build output 75 | .vuepress/dist 76 | 77 | # Serverless directories 78 | .serverless 79 | 80 | # IDE / Editor 81 | .idea 82 | 83 | # Service worker 84 | sw.* 85 | 86 | # macOS 87 | .DS_Store 88 | 89 | # Vim swap files 90 | *.swp 91 | -------------------------------------------------------------------------------- /assets/sass/basics/_bootstrap-override.scss: -------------------------------------------------------------------------------- 1 | // Bootstrap 4 grid override 2 | 3 | // Grid breakpoints 4 | // 5 | // Define the minimum dimensions at which your layout will change, 6 | // adapting to different screen sizes, for use in media queries. 7 | 8 | $grid-breakpoints: ( 9 | xs: 0, 10 | sm: 768px, 11 | md: 992px, 12 | lg: 1300px, 13 | xl: 1500px 14 | ); 15 | 16 | @include _assert-ascending($grid-breakpoints, "$grid-breakpoints"); 17 | // @include _assert-starts-at-zero($grid-breakpoints); 18 | @include _assert-starts-at-zero($grid-breakpoints, "$grid-breakpoints"); 19 | 20 | 21 | // Grid containers 22 | // 23 | // Define the maximum width of `.container` for different screen sizes. 24 | 25 | $container-max-widths: ( 26 | sm: 740px, 27 | md: 970px, 28 | lg: 1250px, 29 | xl: 1440px 30 | ); 31 | 32 | @include _assert-ascending($container-max-widths, "$container-max-widths"); 33 | 34 | 35 | // Grid columns 36 | // 37 | // Set the number of columns and specify the width of the gutters. 38 | 39 | $grid-columns: 12; 40 | $grid-gutter-width: 24px; 41 | $enable-grid-classes: true; 42 | // Spacing 43 | // 44 | // Control the default styling of most Bootstrap elements by modifying these 45 | // variables. Mostly focused on spacing. 46 | // You can add more entries to the $spacers map, should you need more variation. 47 | 48 | $spacer: 1rem !default; 49 | $spacers: () !default; 50 | // stylelint-disable-next-line scss/dollar-variable-default 51 | $spacers: map-merge( 52 | ( 53 | 0: 0, 54 | 1: ($spacer * .25), 55 | 2: ($spacer * .5), 56 | 3: $spacer, 57 | 4: ($spacer * 1.5), 58 | 5: ($spacer * 3), 59 | 6: ($spacer * 4), 60 | 7: ($spacer * 5), 61 | 8: ($spacer * 6), 62 | 9: ($spacer * 7), 63 | 10: ($spacer * 8) 64 | ), 65 | $spacers 66 | ); -------------------------------------------------------------------------------- /pages/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 21 | 22 | 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vaccination Game 2 | > Web game with the goal to eradicate a disease before it spreads throughout the population 3 | 4 | [![License](https://img.shields.io/badge/License-MIT-blue)](#license) 5 | 6 | [![Made with Node.js](https://img.shields.io/badge/Node.js->=12-blue?logo=node.js&logoColor=white)](https://nodejs.org) 7 | [![Package - vue](https://img.shields.io/github/package-json/dependency-version/claudiobonfati/vaccination-game/vue?logo=vue.js&logoColor=white)](https://www.npmjs.com/package/vue) 8 | [![Package - nuxt](https://img.shields.io/github/package-json/dependency-version/claudiobonfati/vaccination-game/nuxt?logo=nuxt.js&logoColor=white)](https://www.npmjs.com/package/nuxt) 9 | 10 | 11 | A web game based on the browser's [Canvas API](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API) and built with [Vue.js](https://vuejs.org/). 12 | 13 | 14 | ## Play the game online 15 | 16 |
17 | 18 | [![View site - GH Pages](https://img.shields.io/badge/Go_to-Vaccination_Game-2ea44f?style=for-the-badge)](https://claudiobonfati.github.io/vaccination-game/) 19 | 20 |
21 | 22 | ## Preview 23 | 24 | [![Website screenshot](https://i.imgur.com/B33FaMT.png "Website screenshot")](https://claudiobonfati.github.io/vaccination-game/) 25 | 26 | ## About this project 27 | 28 | The idea of the game is to show a minimalist and entertaining way of how to eradicate a disease in a population. 29 | 30 | ## Why? 31 | 32 | This project is part of my personal portfolio, I've dedicated this project to learn the fundamentals of canvas drawing, animation, and user/object interactions. 33 | 34 | I'll be more than happy if you could provide me any feedback about the project, code, code structure, and so on! 35 | 36 | Connect with me at [LinkedIn](https://www.linkedin.com/in/claudiobonfati). 37 | 38 | ## Some notes about the project 39 | 40 | 1. _"Keep it simple"_ was one of the goals that I had in mind while developing. 41 | 2. The game was designed using a color palette inspired by an artwork made by [Rudi Hartono](https://dribbble.com/shots/15243102-Game-Match-Recap). 42 | 43 | ## Getting Started 44 | 45 | ### Prerequisites 46 | 47 | To run this project in the development mode, you'll need to have a basic environment to run a Nuxt.js application, which can be found [here](https://nuxtjs.org/docs/2.x/get-started/installation). 48 | 49 | For a detailed explanation of how Nuxt.js works, check out [Nuxt.js docs](https://nuxtjs.org). 50 | 51 | ### Installation 52 | 53 | **Clone the repository** 54 | 55 | ```sh 56 | $ git clone https://github.com/claudiobonfati/vaccination-game.git 57 | $ cd vaccination-game 58 | ``` 59 | 60 | **Install dependencies** 61 | 62 | ```sh 63 | $ npm install 64 | ``` 65 | 66 | ### Run 67 | 68 | With all dependencies installed and the environment properly configured, you can now start the dev server: 69 | 70 | ```sh 71 | $ npm run dev 72 | ``` 73 | 74 | Open in the browser at: 75 | 76 | - http://localhost:3000/vaccination-game/ 77 | 78 | ## Contributing 79 | 80 | You can send as many PRs as you like, I'll be glad to analyze and accept them! 81 | 82 | Connect with me on [LinkedIn](https://www.linkedin.com/in/claudiobonfati). 83 | 84 | Thank you! 85 | 86 | ## Authors 87 | 88 | - **Claudio Bonfati** - [LinkedIn Profile](https://www.linkedin.com/in/claudiobonfati) 89 | 90 | ## License 91 | 92 | This project is licensed under the [MIT License](https://choosealicense.com/licenses/mit/) by [@claudiobonfati](https://github.com/claudiobonfati). 93 | -------------------------------------------------------------------------------- /nuxt.config.js: -------------------------------------------------------------------------------- 1 | 2 | export default { 3 | server: { 4 | // port: 8000, // default: 3000 5 | // host: '192.168.0.103' // default: localhost 6 | }, 7 | mode: 'universal', 8 | /* 9 | ** Global Variables 10 | */ 11 | env: { 12 | baseUrl: process.env.BASE_URL || 'http://localhost:3000/vaccination-game', 13 | }, 14 | router: { 15 | base: '/vaccination-game/', 16 | }, 17 | /* 18 | ** Headers of the page 19 | */ 20 | head: { 21 | titleTemplate: 'Vaccination Game', 22 | meta: [ 23 | { charset: 'utf-8' }, 24 | { name: 'viewport', content: 'width=device-width, initial-scale=1.0' }, 25 | { hid: 'description', name: 'description', content: 'A game that shows minimalistically how to eradicate a disease in a population.' }, 26 | { hid: 'msapplicationtilecolor', name: 'msapplication-TileColor', content: '#0f111b' }, 27 | { hid: 'msapplicationtileimage', name: 'msapplication-TileImage', content: '/favicon/ms-icon-144x144.png' }, 28 | { hid: 'themecolor', name: 'theme-color', content: '#0f111b' }, 29 | { property: 'og:url', content: process.env.BASE_URL || 'http://localhost:3000/vaccination-game/' }, 30 | { property: 'og:title', content: 'Vaccination Game' }, 31 | { property: 'og:description', content: 'A game that shows minimalistically how to eradicate a disease in a population.' }, 32 | { property: 'og:type', content: 'website' }, 33 | { property: 'og:image', content: `${process.env.BASE_URL || ''}/img/vaccination-game-og.png` }, 34 | { property: 'og:image:width', content: '1080' }, 35 | { property: 'og:image:height', content: '1080' }, 36 | { property: 'og:image:type', content: 'image/png' }, 37 | { property: 'og:locale', content: 'pt_BR' }, 38 | ], 39 | link: [ 40 | { rel: 'apple-touch-icon', sizes: '57x57', href: `${process.env.BASE_URL || ''}/img/favicon/apple-icon-57x57.png` }, 41 | { rel: 'apple-touch-icon', sizes: '60x60', href: `${process.env.BASE_URL || ''}/img/favicon/apple-icon-60x60.png` }, 42 | { rel: 'apple-touch-icon', sizes: '72x72', href: `${process.env.BASE_URL || ''}/img/favicon/apple-icon-72x72.png` }, 43 | { rel: 'apple-touch-icon', sizes: '76x76', href: `${process.env.BASE_URL || ''}/img/favicon/apple-icon-76x76.png` }, 44 | { rel: 'apple-touch-icon', sizes: '114x114', href: `${process.env.BASE_URL || ''}/img/favicon/apple-icon-114x114.png` }, 45 | { rel: 'apple-touch-icon', sizes: '120x120', href: `${process.env.BASE_URL || ''}/img/favicon/apple-icon-120x120.png` }, 46 | { rel: 'apple-touch-icon', sizes: '144x144', href: `${process.env.BASE_URL || ''}/img/favicon/apple-icon-144x144.png` }, 47 | { rel: 'apple-touch-icon', sizes: '152x152', href: `${process.env.BASE_URL || ''}/img/favicon/apple-icon-152x152.png` }, 48 | { rel: 'apple-touch-icon', sizes: '180x180', href: `${process.env.BASE_URL || ''}/img/favicon/apple-icon-180x180.png` }, 49 | { rel: 'icon', type: 'image/png', sizes: '192x192', href: `${process.env.BASE_URL || ''}/img/favicon/android-icon-192x192.png` }, 50 | { rel: 'icon', type: 'image/png', sizes: '32x32', href: `${process.env.BASE_URL || ''}/img/favicon/favicon-32x32.png` }, 51 | { rel: 'icon', type: 'image/png', sizes: '96x96', href: `${process.env.BASE_URL || ''}/img/favicon/favicon-96x96.png` }, 52 | { rel: 'icon', type: 'image/png', sizes: '16x16', href: `${process.env.BASE_URL || ''}/img/favicon/favicon-16x16.png` }, 53 | ] 54 | }, 55 | loading: { 56 | color: '#0f111b', 57 | throttle: 0, 58 | }, 59 | css: [], 60 | plugins: [ 61 | { src: `~plugins/feather.js`, ssr: false }, 62 | ], 63 | buildModules: [], 64 | modules: [ 65 | '@nuxtjs/style-resources', 66 | ], 67 | styleResources: { 68 | sass: ['~assets/sass/global.sass'] 69 | }, 70 | build: { 71 | vendor: [], 72 | postcss: { 73 | preset: { 74 | features: { 75 | customProperties: false 76 | } 77 | } 78 | }, 79 | transpile: [ 80 | "gsap" 81 | ], 82 | extend (config, ctx) { 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /utils/GamePerson.js: -------------------------------------------------------------------------------- 1 | import Vec2 from "./Vec2"; 2 | import { Howl, Howler } from 'howler'; 3 | 4 | export default class Person { 5 | constructor(x, y, disease, status, distances, diseaseArms, radius) { 6 | this.center = { 7 | x: x, 8 | y: y, 9 | }; 10 | this.diseaseSpeed = disease; 11 | this.status = status; // 1: Healthy; 2: Imune; 3: Sick; 12 | this.distances = distances; 13 | this.waypoints = { 14 | toTopRight: { 15 | step: 1, 16 | points: null, 17 | }, 18 | toBottomRight: { 19 | step: 1, 20 | points: null, 21 | }, 22 | toBottomLeft: { 23 | step: 1, 24 | points: null, 25 | }, 26 | toTopLeft: { 27 | step: 1, 28 | points: null, 29 | }, 30 | }; 31 | 32 | this.diseaseArms = diseaseArms; 33 | this.surroundPeople = null; 34 | this.radius = radius; 35 | this.shieldSize = 6; 36 | this.maxDistanceX = this.distances.x - this.radius - this.shieldSize; 37 | this.maxDistanceY = this.distances.y - this.radius - this.shieldSize; 38 | 39 | if (this.status === 3) { 40 | this.calcDiagonalWaypoints(); 41 | } 42 | 43 | this.colors = { 44 | healthy: "#83ebce", 45 | healthyTransp:"#283841", 46 | imune: "#EA9B1C", 47 | imuneTransp: "rgba(234, 155, 28, 0.2)", 48 | sick: "#e36037", 49 | sickTransp: "rgba(225, 87, 64, 0.2)", 50 | bg: "#030710", 51 | diseaseArms: "#de3533" 52 | } 53 | } 54 | 55 | calcDiagonalWaypoints() { 56 | let tempVector; 57 | 58 | 59 | if (this.diseaseArms.topRight.enabled) { 60 | let vertToTopRight = [ 61 | { 62 | x: this.center.x, 63 | y: this.center.y, 64 | }, { 65 | x: this.center.x + this.distances.x, 66 | y: this.center.y - this.distances.y, 67 | } 68 | ]; 69 | tempVector = new Vec2( 70 | vertToTopRight[1].x - vertToTopRight[0].x, 71 | vertToTopRight[1].y - vertToTopRight[0].y, 72 | ); 73 | tempVector.normalize(); 74 | vertToTopRight[1].x = vertToTopRight[1].x - ((this.radius + this.shieldSize) * tempVector.x); 75 | vertToTopRight[1].y = vertToTopRight[1].y - ((this.radius + this.shieldSize) * tempVector.y); 76 | 77 | this.waypoints.toTopRight.points = this.calcWaypoints(vertToTopRight); 78 | } 79 | 80 | if (this.diseaseArms.bottomRight.enabled) { 81 | let vertToBottomRight = [ 82 | { 83 | x: this.center.x, 84 | y: this.center.y 85 | }, { 86 | x: this.center.x + this.distances.x, 87 | y: this.center.y + this.distances.y, 88 | } 89 | ]; 90 | tempVector = new Vec2( 91 | vertToBottomRight[1].x - vertToBottomRight[0].x, 92 | vertToBottomRight[1].y - vertToBottomRight[0].y, 93 | ); 94 | tempVector.normalize(); 95 | vertToBottomRight[1].x = vertToBottomRight[1].x - ((this.radius + this.shieldSize) * tempVector.x); 96 | vertToBottomRight[1].y = vertToBottomRight[1].y - ((this.radius + this.shieldSize) * tempVector.y); 97 | 98 | this.waypoints.toBottomRight.points = this.calcWaypoints(vertToBottomRight); 99 | } 100 | 101 | if (this.diseaseArms.bottomLeft.enabled) { 102 | let vertToBottomLeft = [ 103 | { 104 | x: this.center.x, 105 | y: this.center.y 106 | }, { 107 | x: this.center.x - this.distances.x, 108 | y: this.center.y + this.distances.y, 109 | } 110 | ]; 111 | tempVector = new Vec2( 112 | vertToBottomLeft[1].x - vertToBottomLeft[0].x, 113 | vertToBottomLeft[1].y - vertToBottomLeft[0].y, 114 | ); 115 | tempVector.normalize(); 116 | vertToBottomLeft[1].x = vertToBottomLeft[1].x - ((this.radius + this.shieldSize) * tempVector.x); 117 | vertToBottomLeft[1].y = vertToBottomLeft[1].y - ((this.radius + this.shieldSize) * tempVector.y); 118 | 119 | this.waypoints.toBottomLeft.points = this.calcWaypoints(vertToBottomLeft); 120 | } 121 | 122 | if (this.diseaseArms.topLeft.enabled) { 123 | let vertToTopLeft = [ 124 | { 125 | x: this.center.x, 126 | y: this.center.y 127 | }, { 128 | x: this.center.x - this.distances.x, 129 | y: this.center.y - this.distances.y, 130 | } 131 | ]; 132 | tempVector = new Vec2( 133 | vertToTopLeft[1].x - vertToTopLeft[0].x, 134 | vertToTopLeft[1].y - vertToTopLeft[0].y, 135 | ); 136 | tempVector.normalize(); 137 | vertToTopLeft[1].x = vertToTopLeft[1].x - ((this.radius + this.shieldSize) * tempVector.x); 138 | vertToTopLeft[1].y = vertToTopLeft[1].y - ((this.radius + this.shieldSize) * tempVector.y); 139 | 140 | this.waypoints.toTopLeft.points = this.calcWaypoints(vertToTopLeft); 141 | } 142 | } 143 | 144 | calcWaypoints(vertices, points = 100) { 145 | let waypoints = []; 146 | for(let i = 1; i < vertices.length; i++) { 147 | let pt0 = vertices[i - 1]; 148 | let pt1 = vertices[i]; 149 | let dx = pt1.x - pt0.x; 150 | let dy = pt1.y - pt0.y; 151 | for(let j = 0; j < points; j++) { 152 | let x = pt0.x + dx * j / points; 153 | let y = pt0.y + dy * j / points; 154 | waypoints.push({ 155 | x: x, 156 | y: y, 157 | }); 158 | } 159 | } 160 | 161 | return(waypoints); 162 | } 163 | 164 | draw(ctx) { 165 | ctx.lineCap = "round"; 166 | ctx.lineWidth = 2; 167 | 168 | if (this.status === 3) { 169 | // DiseaseArm Right 170 | if (this.diseaseArms.right.enabled) { 171 | if (this.diseaseArms.right.size < this.maxDistanceX) { 172 | ctx.beginPath(); 173 | ctx.moveTo( 174 | this.center.x, 175 | this.center.y 176 | ); 177 | ctx.lineTo( 178 | this.center.x + Math.floor(this.diseaseArms.right.size), 179 | this.center.y 180 | ); 181 | ctx.strokeStyle = this.colors.diseaseArms; 182 | ctx.stroke(); 183 | 184 | this.diseaseArms.right.size += (this.maxDistanceX / this.diseaseSpeed.right); 185 | } else { 186 | ctx.beginPath(); 187 | ctx.moveTo(this.center.x, this.center.y); 188 | ctx.lineTo(this.center.x + this.diseaseArms.right.size, this.center.y); 189 | ctx.strokeStyle = this.colors.diseaseArms; 190 | ctx.stroke(); 191 | 192 | if (!this.diseaseArms.right.infected) { 193 | this.surroundPeople.find(x => x.position === "right").person.tryToInfect(); 194 | this.diseaseArms.right.infected = true; 195 | } 196 | } 197 | } 198 | 199 | // DiseaseArm Left 200 | if (this.diseaseArms.left.enabled) { 201 | if (this.diseaseArms.left.size < this.maxDistanceX) { 202 | ctx.beginPath(); 203 | ctx.moveTo( 204 | this.center.x, 205 | this.center.y 206 | ); 207 | ctx.lineTo( 208 | this.center.x - Math.floor(this.diseaseArms.left.size), 209 | this.center.y 210 | ); 211 | ctx.strokeStyle = this.colors.diseaseArms; 212 | ctx.stroke(); 213 | 214 | this.diseaseArms.left.size += (this.maxDistanceX / this.diseaseSpeed.left); 215 | } else { 216 | ctx.beginPath(); 217 | ctx.moveTo(this.center.x, this.center.y); 218 | ctx.lineTo(this.center.x - this.diseaseArms.left.size, this.center.y); 219 | ctx.strokeStyle = this.colors.diseaseArms; 220 | ctx.stroke(); 221 | 222 | if (!this.diseaseArms.left.infected) { 223 | this.surroundPeople.find(x => x.position === "left").person.tryToInfect(); 224 | this.diseaseArms.left.infected = true; 225 | } 226 | } 227 | } 228 | 229 | // DiseaseArm Top 230 | if (this.diseaseArms.top.enabled) { 231 | if (this.diseaseArms.top.size < this.maxDistanceY) { 232 | ctx.beginPath(); 233 | ctx.moveTo( 234 | this.center.x, 235 | this.center.y 236 | ); 237 | ctx.lineTo( 238 | this.center.x, 239 | this.center.y - Math.floor(this.diseaseArms.top.size) 240 | ); 241 | ctx.strokeStyle = this.colors.diseaseArms; 242 | ctx.stroke(); 243 | 244 | this.diseaseArms.top.size += (this.maxDistanceY / this.diseaseSpeed.top); 245 | } else { 246 | ctx.beginPath(); 247 | ctx.moveTo(this.center.x, this.center.y); 248 | ctx.lineTo(this.center.x, this.center.y - this.maxDistanceY); 249 | ctx.strokeStyle = this.colors.diseaseArms; 250 | ctx.stroke(); 251 | 252 | if (!this.diseaseArms.top.infected) { 253 | this.surroundPeople.find(x => x.position === "top").person.tryToInfect(); 254 | this.diseaseArms.top.infected = true; 255 | } 256 | } 257 | } 258 | 259 | // DiseaseArm Bottom 260 | if (this.diseaseArms.bottom.enabled) { 261 | if (this.diseaseArms.bottom.size < this.maxDistanceY) { 262 | ctx.beginPath(); 263 | ctx.moveTo( 264 | this.center.x, 265 | this.center.y 266 | ); 267 | ctx.lineTo( 268 | this.center.x, 269 | this.center.y + Math.floor(this.diseaseArms.bottom.size) 270 | ); 271 | ctx.strokeStyle = this.colors.diseaseArms; 272 | ctx.stroke(); 273 | 274 | this.diseaseArms.bottom.size += (this.maxDistanceY / this.diseaseSpeed.bottom); 275 | } else { 276 | ctx.beginPath(); 277 | ctx.moveTo(this.center.x, this.center.y); 278 | ctx.lineTo(this.center.x, this.center.y + this.maxDistanceY); 279 | ctx.strokeStyle = this.colors.diseaseArms; 280 | ctx.stroke(); 281 | 282 | if (!this.diseaseArms.bottom.infected) { 283 | this.surroundPeople.find(x => x.position === "bottom").person.tryToInfect(); 284 | this.diseaseArms.bottom.infected = true; 285 | } 286 | } 287 | } 288 | 289 | // DiseaseArm Top-Right 290 | if (this.diseaseArms.topRight.enabled) { 291 | if (this.waypoints.toTopRight.step < 99) { 292 | ctx.beginPath(); 293 | ctx.moveTo( 294 | this.waypoints.toTopRight.points[0].x, 295 | this.waypoints.toTopRight.points[0].y 296 | ); 297 | ctx.lineTo( 298 | this.waypoints.toTopRight.points[Math.floor(this.waypoints.toTopRight.step)].x, 299 | this.waypoints.toTopRight.points[Math.floor(this.waypoints.toTopRight.step)].y 300 | ); 301 | ctx.strokeStyle = this.colors.diseaseArms; 302 | ctx.stroke(); 303 | 304 | this.waypoints.toTopRight.step += this.waypoints.toTopRight.points.length / this.diseaseSpeed.topRight; 305 | } else { 306 | ctx.beginPath(); 307 | ctx.moveTo(this.waypoints.toTopRight.points[0].x, this.waypoints.toTopRight.points[0].y); 308 | ctx.lineTo(this.waypoints.toTopRight.points[99].x, this.waypoints.toTopRight.points[99].y); 309 | ctx.lineWidth = 2; 310 | ctx.strokeStyle = this.colors.diseaseArms; 311 | ctx.stroke(); 312 | 313 | if (!this.diseaseArms.topRight.infected) { 314 | this.surroundPeople.find(x => x.position === "topRight").person.tryToInfect(); 315 | this.diseaseArms.topRight.infected = true; 316 | } 317 | } 318 | } 319 | 320 | // DiseaseArm Bottom-Right 321 | if (this.diseaseArms.bottomRight.enabled) { 322 | if (this.waypoints.toBottomRight.step < 99) { 323 | ctx.beginPath(); 324 | ctx.moveTo( 325 | this.waypoints.toBottomRight.points[0].x, 326 | this.waypoints.toBottomRight.points[0].y 327 | ); 328 | ctx.lineTo( 329 | this.waypoints.toBottomRight.points[Math.floor(this.waypoints.toBottomRight.step)].x, 330 | this.waypoints.toBottomRight.points[Math.floor(this.waypoints.toBottomRight.step)].y 331 | ); 332 | ctx.strokeStyle = this.colors.diseaseArms; 333 | ctx.stroke(); 334 | 335 | this.waypoints.toBottomRight.step += this.waypoints.toBottomRight.points.length / this.diseaseSpeed.bottomRight; 336 | } else { 337 | ctx.beginPath(); 338 | ctx.moveTo(this.waypoints.toBottomRight.points[0].x, this.waypoints.toBottomRight.points[0].y); 339 | ctx.lineTo(this.waypoints.toBottomRight.points[99].x, this.waypoints.toBottomRight.points[99].y); 340 | ctx.strokeStyle = this.colors.diseaseArms; 341 | ctx.stroke(); 342 | 343 | if (!this.diseaseArms.bottomRight.infected) { 344 | this.surroundPeople.find(x => x.position === "bottomRight").person.tryToInfect(); 345 | this.diseaseArms.bottomRight.infected = true; 346 | } 347 | } 348 | } 349 | 350 | // DiseaseArm Bottom-Left 351 | if (this.diseaseArms.bottomLeft.enabled) { 352 | if (this.waypoints.toBottomLeft.step < 99) { 353 | ctx.beginPath(); 354 | ctx.moveTo( 355 | this.waypoints.toBottomLeft.points[0].x, 356 | this.waypoints.toBottomLeft.points[0].y 357 | ); 358 | ctx.lineTo( 359 | this.waypoints.toBottomLeft.points[Math.floor(this.waypoints.toBottomLeft.step)].x, 360 | this.waypoints.toBottomLeft.points[Math.floor(this.waypoints.toBottomLeft.step)].y 361 | ); 362 | ctx.strokeStyle = this.colors.diseaseArms; 363 | ctx.stroke(); 364 | 365 | this.waypoints.toBottomLeft.step += this.waypoints.toBottomLeft.points.length / this.diseaseSpeed.bottomLeft; 366 | } else { 367 | ctx.beginPath(); 368 | ctx.moveTo(this.waypoints.toBottomLeft.points[0].x, this.waypoints.toBottomLeft.points[0].y); 369 | ctx.lineTo(this.waypoints.toBottomLeft.points[99].x, this.waypoints.toBottomLeft.points[99].y); 370 | ctx.strokeStyle = this.colors.diseaseArms; 371 | ctx.stroke(); 372 | 373 | if (!this.diseaseArms.bottomLeft.infected) { 374 | this.surroundPeople.find(x => x.position === "bottomLeft").person.tryToInfect(); 375 | this.diseaseArms.bottomLeft.infected = false; 376 | } 377 | } 378 | } 379 | 380 | // DiseaseArm Top-Left 381 | if (this.diseaseArms.topLeft.enabled) { 382 | if (this.waypoints.toTopLeft.step < 99) { 383 | ctx.beginPath(); 384 | ctx.moveTo( 385 | this.waypoints.toTopLeft.points[0].x, 386 | this.waypoints.toTopLeft.points[0].y 387 | ); 388 | ctx.lineTo( 389 | this.waypoints.toTopLeft.points[Math.floor(this.waypoints.toTopLeft.step)].x, 390 | this.waypoints.toTopLeft.points[Math.floor(this.waypoints.toTopLeft.step)].y 391 | ); 392 | ctx.lineWidth = 2; 393 | ctx.strokeStyle = this.colors.diseaseArms; 394 | ctx.stroke(); 395 | 396 | this.waypoints.toTopLeft.step += this.waypoints.toTopLeft.points.length / this.diseaseSpeed.topLeft; 397 | } else { 398 | ctx.beginPath(); 399 | ctx.moveTo(this.waypoints.toTopLeft.points[0].x, this.waypoints.toTopLeft.points[0].y); 400 | ctx.lineTo(this.waypoints.toTopLeft.points[99].x, this.waypoints.toTopLeft.points[99].y); 401 | ctx.strokeStyle = this.colors.diseaseArms; 402 | ctx.stroke(); 403 | 404 | if (!this.diseaseArms.topLeft.infected) { 405 | this.surroundPeople.find(x => x.position === "topLeft").person.tryToInfect(); 406 | this.diseaseArms.topLeft.infected = true; 407 | } 408 | } 409 | } 410 | } 411 | 412 | // Drawing Person 413 | if (this.status === 1) { 414 | // Head 415 | ctx.beginPath(); 416 | ctx.arc( 417 | this.center.x, 418 | this.center.y, 419 | this.radius, 420 | 0, 421 | Math.PI * 2, 422 | false 423 | ); 424 | ctx.lineWidth = 5; 425 | ctx.strokeStyle = this.colors.healthy; 426 | ctx.stroke(); 427 | ctx.fillStyle = this.colors.healthyTransp; 428 | ctx.fill(); 429 | 430 | // Left eye 431 | ctx.beginPath(); 432 | ctx.arc(this.center.x - (this.radius / 3), this.center.y - (this.radius / 4), 2, 0, Math.PI * 2, false); 433 | ctx.fillStyle = this.colors.healthy; 434 | ctx.fill(); 435 | 436 | // Right eye 437 | ctx.beginPath(); 438 | ctx.arc(this.center.x + (this.radius / 3), this.center.y - (this.radius / 4), 2, 0, Math.PI * 2, false); 439 | ctx.fillStyle = this.colors.healthy; 440 | ctx.fill(); 441 | 442 | // Mouth 443 | ctx.beginPath(); 444 | ctx.moveTo( 445 | this.center.x - (this.radius / 2.5), // Starting Point 446 | this.center.y + (this.radius / 3), // Starting Point 447 | ); 448 | ctx.lineTo( 449 | this.center.x + (this.radius / 2.5), // Ending Point 450 | this.center.y + (this.radius / 3), // Ending Point 451 | ); 452 | ctx.lineWidth = 2; 453 | ctx.strokeStyle = this.colors.healthy; 454 | ctx.stroke(); 455 | } else if (this.status === 2) { 456 | // Head 457 | ctx.beginPath(); 458 | ctx.arc( 459 | this.center.x, 460 | this.center.y, 461 | this.radius, 462 | 0, 463 | Math.PI * 2, 464 | false 465 | ); 466 | ctx.lineWidth = 5; 467 | ctx.strokeStyle = this.colors.imune; 468 | ctx.stroke(); 469 | ctx.fillStyle = this.colors.imune; 470 | ctx.fill(); 471 | 472 | ctx.beginPath(); 473 | ctx.arc( 474 | this.center.x, 475 | this.center.y, 476 | this.radius + 6, 477 | 0, 478 | Math.PI * 2, 479 | false 480 | ); 481 | ctx.fillStyle = this.colors.imuneTransp; 482 | ctx.fill(); 483 | 484 | // Left eye 485 | ctx.beginPath(); 486 | ctx.arc(this.center.x - (this.radius / 3), this.center.y - (this.radius / 3), 2, 0, Math.PI * 2, false); 487 | ctx.fillStyle = this.colors.bg; 488 | ctx.fill(); 489 | 490 | // Right eye 491 | ctx.beginPath(); 492 | ctx.arc(this.center.x + (this.radius / 3), this.center.y - (this.radius / 3), 2, 0, Math.PI * 2, false); 493 | ctx.fillStyle = this.colors.bg; 494 | ctx.fill(); 495 | 496 | // Mouth 497 | ctx.beginPath(); 498 | ctx.moveTo( 499 | this.center.x - (this.radius / 2), // Starting Point 500 | this.center.y + (this.radius / 5), // Starting Point 501 | ); 502 | ctx.bezierCurveTo( 503 | this.center.x - (this.radius / 2.5), // Force Curve Starting Point 504 | this.center.y + (this.radius / 1.5), // Force Curve Starting Point 505 | 506 | this.center.x + (this.radius / 2.5), // Force Curve Ending Point 507 | this.center.y + (this.radius / 1.5), // Force Curve Ending Point 508 | 509 | this.center.x + (this.radius / 2), // Ending Point 510 | this.center.y + (this.radius / 5), // Ending Point 511 | ); 512 | ctx.lineWidth = 2; 513 | ctx.strokeStyle = this.colors.bg; 514 | ctx.stroke(); 515 | } else if (this.status === 3) { 516 | // Head 517 | ctx.beginPath(); 518 | ctx.arc( 519 | this.center.x + (Math.floor(Math.random() * 2) - 0.5), 520 | this.center.y + (Math.floor(Math.random() * 2) - 0.5), 521 | this.radius, 522 | 0, 523 | Math.PI * 2, 524 | false 525 | ); 526 | ctx.lineWidth = 5; 527 | ctx.strokeStyle = this.colors.sick; 528 | ctx.stroke(); 529 | ctx.fillStyle = this.colors.sick; 530 | ctx.fill(); 531 | 532 | ctx.beginPath(); 533 | ctx.arc( 534 | this.center.x, 535 | this.center.y, 536 | this.radius + 6, 537 | 0, 538 | Math.PI * 2, 539 | false 540 | ); 541 | ctx.fillStyle = this.colors.sickTransp; 542 | ctx.fill(); 543 | 544 | // Left eye 545 | ctx.beginPath(); 546 | ctx.arc(this.center.x - (this.radius / 3), this.center.y - (this.radius / 3), 2.5, 0, Math.PI * 2, false); 547 | ctx.fillStyle = this.colors.bg; 548 | ctx.fill(); 549 | 550 | // Right eye 551 | ctx.beginPath(); 552 | ctx.arc(this.center.x + (this.radius / 3), this.center.y - (this.radius / 3), 2.5, 0, Math.PI * 2, false); 553 | ctx.fillStyle = this.colors.bg; 554 | ctx.fill(); 555 | 556 | // Mouth 557 | ctx.beginPath(); 558 | ctx.moveTo( 559 | this.center.x - (this.radius / 2), // Starting Point 560 | this.center.y + (this.radius / 2), // Starting Point 561 | ); 562 | ctx.bezierCurveTo( 563 | this.center.x - (this.radius / 2.5), // Force Curve Starting Point 564 | this.center.y + (this.radius / 5), // Force Curve Starting Point 565 | 566 | this.center.x + (this.radius / 2.5), // Force Curve Ending Point 567 | this.center.y + (this.radius / 5), // Force Curve Ending Point 568 | 569 | this.center.x + (this.radius / 2), // Ending Point 570 | this.center.y + (this.radius / 2), // Ending Point 571 | ); 572 | ctx.lineWidth = 2; 573 | ctx.strokeStyle = this.colors.bg; 574 | ctx.stroke(); 575 | } 576 | } 577 | 578 | update(ctx) { 579 | this.draw(ctx); 580 | return this.status; 581 | } 582 | 583 | applyVaccine() { 584 | if (this.status === 1) { 585 | this.status = 2; 586 | return true; 587 | } else { 588 | return false; 589 | } 590 | } 591 | 592 | getDistance(x1, y1, x2, y2) { 593 | const xDist = x2 - x1; 594 | const yDist = y2 - y1; 595 | 596 | return Math.sqrt(Math.pow(xDist, 2) + Math.pow(yDist, 2)); 597 | } 598 | 599 | setMounted(surroundPeople) { 600 | this.surroundPeople = surroundPeople; 601 | } 602 | 603 | tryToInfect() { 604 | if (this.status === 1) { 605 | this.status = 3; 606 | this.calcDiagonalWaypoints(); 607 | } 608 | } 609 | } 610 | -------------------------------------------------------------------------------- /components/Game.vue: -------------------------------------------------------------------------------- 1 | 105 | 106 | 708 | 709 | 988 | --------------------------------------------------------------------------------