├── 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 |
2 |
3 |
4 |
5 |
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 |
2 |
8 |
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)
5 |
6 | [](https://nodejs.org)
7 | [](https://www.npmjs.com/package/vue)
8 | [](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 | [](https://claudiobonfati.github.io/vaccination-game/)
19 |
20 |
21 |
22 | ## Preview
23 |
24 | [](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 |
2 |
3 |
4 |
5 |
FIRST
6 |
Trap the disease as fast as possible and maybe you will have a chance.
7 |
8 |
9 |
THEN
10 |
Vaccine all remaining people before the timer goes out.
11 |
12 |
13 |
48 |
49 |
50 |
51 |
52 |
53 | Play
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | Continue
63 |
64 |
65 |
66 | Restart
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | {{ content.win.title }}
76 |
77 |
78 |
79 |
80 | Play again
81 |
82 |
83 | Learn more
84 |
85 |
86 |
87 |
88 | {{ content.lose.title }}
89 |
90 |
91 |
92 |
93 | Try again
94 |
95 |
96 | Learn more
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
708 |
709 |
988 |
--------------------------------------------------------------------------------