├── sandbox.config.json
├── src
├── events.js
├── dumpMap.js
├── main.js
├── const.js
├── actions.js
├── health.js
└── component.js
├── index.html
├── demo
├── assets
│ ├── animations
│ │ ├── elves-craft-pixel.png
│ │ └── elves-craft-pixel.json
│ └── pics
│ │ └── fairy-background-craft-pixel.png
├── .eslintrc.yml
├── index.html
└── demo.js
├── .eslintrc.yml
├── tests
├── .eslintrc.yml
├── index.html
└── tests.js
├── rollup.config.js
├── CHANGELOG.md
├── package.json
├── README.md
└── dist
├── phaser-component-health.esm.js
└── phaser-component-health.umd.js
/sandbox.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "template": "static"
3 | }
--------------------------------------------------------------------------------
/src/events.js:
--------------------------------------------------------------------------------
1 | export { DAMAGE, DIE, HEAL, HEALTH_CHANGE, REVIVE } from './const';
2 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Demo
4 | Tests
--------------------------------------------------------------------------------
/demo/assets/animations/elves-craft-pixel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/samme/phaser-component-health/HEAD/demo/assets/animations/elves-craft-pixel.png
--------------------------------------------------------------------------------
/demo/assets/pics/fairy-background-craft-pixel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/samme/phaser-component-health/HEAD/demo/assets/pics/fairy-background-craft-pixel.png
--------------------------------------------------------------------------------
/demo/.eslintrc.yml:
--------------------------------------------------------------------------------
1 | extends: 'semistandard'
2 | env:
3 | browser: true
4 | es6: true
5 | globals:
6 | Phaser: false
7 | PhaserHealth: false
8 | rules:
9 | no-console: off
10 |
--------------------------------------------------------------------------------
/src/dumpMap.js:
--------------------------------------------------------------------------------
1 | export const dumpMap = function (obj) {
2 | return {
3 | name: obj.name,
4 | alive: obj.isAlive(),
5 | health: obj.getHealth(),
6 | minHealth: obj.getMinHealth(),
7 | maxHealth: obj.getMaxHealth()
8 | };
9 | };
10 |
--------------------------------------------------------------------------------
/.eslintrc.yml:
--------------------------------------------------------------------------------
1 | extends: semistandard
2 | env:
3 | browser: yes
4 | es6: yes
5 | globals:
6 | Atomics: readonly
7 | SharedArrayBuffer: readonly
8 | parserOptions:
9 | ecmaVersion: 2018
10 | rules:
11 | no-console:
12 | - warn
13 | - allow:
14 | - table
15 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 |
2 | import * as Actions from './actions';
3 | import * as Health from './health';
4 | import * as HealthComponent from './component';
5 | import * as Events from './events';
6 |
7 | export default {
8 | ...Health,
9 | Actions,
10 | Events,
11 | HealthComponent
12 | };
13 |
--------------------------------------------------------------------------------
/tests/.eslintrc.yml:
--------------------------------------------------------------------------------
1 | extends: '../.eslintrc.yml'
2 | env:
3 | mocha: yes
4 | globals:
5 | chai: readonly
6 | Phaser: readonly
7 | PhaserHealth: readonly
8 | rules:
9 | no-console:
10 | - warn
11 | - allow:
12 | - log
13 | - table
14 | no-unused-expressions: off
15 |
--------------------------------------------------------------------------------
/src/const.js:
--------------------------------------------------------------------------------
1 | export const DAMAGE = 'damage';
2 | export const DIE = 'die';
3 | export const HEAL = 'heal';
4 | export const HEALTH = 'health';
5 | export const HEALTH_CHANGE = 'healthchange';
6 | export const MAX_HEALTH = 'maxHealth';
7 | export const MIN_HEALTH = 'minHealth';
8 | export const REVIVE = 'revive';
9 |
--------------------------------------------------------------------------------
/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Demo of Phaser 3 Health Component
4 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import resolve from 'rollup-plugin-node-resolve';
2 | import commonjs from 'rollup-plugin-commonjs';
3 | import buble from 'rollup-plugin-buble';
4 | import pkg from './package.json';
5 |
6 | export default [
7 | {
8 | input: 'src/main.js',
9 | output: [
10 | {
11 | name: 'PhaserHealth',
12 | file: pkg.browser,
13 | format: 'umd',
14 | globals: { phaser: 'Phaser' }
15 | },
16 | {
17 | file: pkg.module,
18 | format: 'es'
19 | }
20 | ],
21 | external: ['phaser'],
22 | plugins: [
23 | resolve(),
24 | commonjs(),
25 | buble({ exclude: ['node_modules/**'], objectAssign: 'Object.assign' })
26 | ]
27 | }
28 | ];
29 |
--------------------------------------------------------------------------------
/tests/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Tests of Phaser 3 Health Component
4 |
5 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/actions.js:
--------------------------------------------------------------------------------
1 | import Phaser from 'phaser';
2 |
3 | import * as Health from './health';
4 |
5 | const { Each } = Phaser.Utils.Array;
6 |
7 | export const Damage = function (objs, amount, silent) {
8 | Each(objs, Health.Damage, null, amount, silent);
9 |
10 | return objs;
11 | };
12 |
13 | export const Heal = function (objs, amount, silent) {
14 | Each(objs, Health.Heal, null, amount, silent);
15 |
16 | return objs;
17 | };
18 |
19 | export const Kill = function (objs, silent) {
20 | Each(objs, Health.Kill, null, silent);
21 |
22 | return objs;
23 | };
24 |
25 | export const Revive = function (objs, health, silent) {
26 | Each(objs, Health.Revive, null, health, silent);
27 |
28 | return objs;
29 | };
30 |
31 | export const ReviveAtMaxHealth = function (objs, silent) {
32 | Each(objs, Health.ReviveAtMaxHealth, null, silent);
33 |
34 | return objs;
35 | };
36 |
37 | export const SetHealth = function (objs, health, maxHealth, silent) {
38 | Each(objs, Health.SetHealth, null, health, maxHealth, silent);
39 |
40 | return objs;
41 | };
42 |
--------------------------------------------------------------------------------
/src/health.js:
--------------------------------------------------------------------------------
1 |
2 | import * as HealthComponent from './component';
3 |
4 | import { dumpMap } from './dumpMap';
5 |
6 | export const AddTo = function (obj, health = 1, minHealth = -Infinity, maxHealth = 100) {
7 | Object.assign(obj, HealthComponent);
8 |
9 | SetHealth(obj, health, minHealth, maxHealth, true);
10 |
11 | return obj;
12 | };
13 |
14 | export const Damage = function (obj, amount, silent) {
15 | return obj.damage(amount, silent);
16 | };
17 |
18 | export const Dump = function (objs) {
19 | console.table(objs.map(dumpMap));
20 | };
21 |
22 | export const Heal = function (obj, amount, silent) {
23 | return obj.heal(amount, silent);
24 | };
25 |
26 | export const IsAlive = function (obj) {
27 | return obj.isAlive();
28 | };
29 |
30 | export const IsDead = function (obj) {
31 | return obj.isDead();
32 | };
33 |
34 | export const Kill = function (obj, silent) {
35 | return obj.kill(silent);
36 | };
37 |
38 | export const MixinTo = function (obj) {
39 | return Object.assign(obj.prototype, HealthComponent);
40 | };
41 |
42 | export const Revive = function (obj, health, silent) {
43 | return obj.revive(health, silent);
44 | };
45 |
46 | export const ReviveAtMaxHealth = function (obj, silent) {
47 | return obj.reviveAtMaxHealth(silent);
48 | };
49 |
50 | export const SetHealth = function (obj, health, minHealth, maxHealth, silent) {
51 | return obj.setHealth(health, minHealth, maxHealth, silent);
52 | };
53 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | 3.0.0
2 | -----
3 |
4 | - Removed: CommonJS script. Use the UMD script (main) instead.
5 | - Changed: [dist](./dist) script names
6 | - Changed: AddTo() arguments
7 | - Changed: SetHealth() arguments
8 | - Changed: Actions.SetHealth() arguments
9 | - Changed: HealthComponent.setHealth() arguments
10 | - Changed: `healthchange` event callback arguments
11 | - Changed: `damage` event fires after `healthchange`, for any change less than 0
12 | - Changed: `heal` event fires after `healthchange`, for any change greater than 0
13 | - Added: HealthComponent.getHealthFrac()
14 | - Added: HealthComponent.getMinHealth()
15 | - Added: HealthComponent.setMinHealth()
16 | - Fixed: import Phaser in ES module build
17 |
18 | 2.0.3
19 | -----
20 |
21 | - Updated dev dependencies.
22 |
23 | 2.0.2
24 | -----
25 |
26 | - Made Phaser a peer dependency (#1).
27 |
28 | 2.0.1
29 | -----
30 |
31 | - Fixed missing `silent` argument in PhaserHealth.Damage().
32 | - Fixed missing `silent` argument in PhaserHealth.Heal().
33 |
34 | 2.0.0
35 | -----
36 |
37 | - Renamed main script to `dist/PhaserHealth.js`.
38 | - Removed `Phaser.Health`. Use `PhaserHealth` global instead (or the default export).
39 | - Kill methods now affect only living targets.
40 | - Revive methods now affect only dead targets.
41 |
42 | 1.2.0
43 | -----
44 |
45 | - Added lots of methods.
46 | - Added Events namespace.
47 |
48 | 1.1.0
49 | -----
50 |
51 | - Fixed inconsistent return values in damage() and heal().
52 | - Added `silent` argument to setMaxHealth().
53 |
54 | 1.0.0
55 | -----
56 |
57 | - npm release.
58 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "phaser-component-health",
3 | "version": "3.0.0",
4 | "description": "Health methods and events for Phaser 3",
5 | "main": "dist/phaser-component-health.umd.js",
6 | "module": "dist/phaser-component-health.esm.js",
7 | "browser": "dist/phaser-component-health.umd.js",
8 | "files": [
9 | "dist",
10 | "src"
11 | ],
12 | "scripts": {
13 | "build": "rollup -c",
14 | "postbuild": "node -c dist/phaser-component-health.umd.js",
15 | "clean": "rm dist/*.js",
16 | "dev": "rollup -c -w",
17 | "preversion": "npm run build && git add dist/*.js",
18 | "test": "eslint src"
19 | },
20 | "repository": {
21 | "type": "git",
22 | "url": "git+https://github.com/samme/phaser-component-health.git"
23 | },
24 | "keywords": [
25 | "phaser",
26 | "phaser3"
27 | ],
28 | "author": "samme",
29 | "license": "ISC",
30 | "devDependencies": {
31 | "acorn": "^6.4.0",
32 | "chai": "^4.2.0",
33 | "chai-spies": "^1.0.0",
34 | "eslint": "^6.7.0",
35 | "eslint-config-semistandard": "^15.0.0",
36 | "eslint-config-standard": "^13.0.1",
37 | "eslint-plugin-import": "^2.18.2",
38 | "eslint-plugin-node": "^10.0.0",
39 | "eslint-plugin-promise": "^4.2.1",
40 | "eslint-plugin-standard": "^4.0.1",
41 | "mocha": "^11.1.0",
42 | "phaser": "3.24.1",
43 | "rollup": "^4.34.6",
44 | "rollup-plugin-buble": "^0.19.6",
45 | "rollup-plugin-commonjs": "^9.2.0",
46 | "rollup-plugin-node-resolve": "^4.0.0"
47 | },
48 | "peerDependencies": {
49 | "phaser": "^3.16.2"
50 | },
51 | "bugs": {
52 | "url": "https://github.com/samme/phaser-component-health/issues"
53 | },
54 | "homepage": "https://github.com/samme/phaser-component-health#readme"
55 | }
56 |
--------------------------------------------------------------------------------
/src/component.js:
--------------------------------------------------------------------------------
1 | import Phaser from 'phaser';
2 |
3 | import { DAMAGE, DIE, HEAL, HEALTH, HEALTH_CHANGE, MAX_HEALTH, MIN_HEALTH, REVIVE } from './const';
4 |
5 | const { Clamp } = Phaser.Math;
6 |
7 | export const getHealth = function () {
8 | return this.getData(HEALTH);
9 | };
10 |
11 | export const getHealthFrac = function () {
12 | return this.getHealth() / this.getMaxHealth();
13 | };
14 |
15 | export const getMinHealth = function () {
16 | return this.getData(MIN_HEALTH);
17 | };
18 |
19 | export const getMaxHealth = function () {
20 | return this.getData(MAX_HEALTH);
21 | };
22 |
23 | export const setHealth = function (health, minHealth, maxHealth, silent) {
24 | if (minHealth !== undefined) {
25 | this.setMinHealth(minHealth, true);
26 | }
27 |
28 | if (maxHealth !== undefined) {
29 | this.setMaxHealth(maxHealth, true);
30 | }
31 |
32 | minHealth = this.getMinHealth();
33 | maxHealth = this.getMaxHealth();
34 |
35 | const prevHealth = this.getHealth();
36 | const newHealth = Clamp(health, minHealth, maxHealth);
37 | const change = newHealth - prevHealth;
38 |
39 | if (change === 0) return this;
40 |
41 | this.setData(HEALTH, newHealth);
42 |
43 | if (silent) return this;
44 |
45 | this.emit(HEALTH_CHANGE, this, change, newHealth, minHealth, maxHealth);
46 |
47 | if (change > 0) {
48 | this.emit(HEAL, this, change);
49 | } else {
50 | this.emit(DAMAGE, this, -change);
51 | }
52 |
53 | if (prevHealth > 0 && newHealth <= 0) {
54 | this.emit(DIE, this);
55 | } else if (prevHealth <= 0 && newHealth > 0) {
56 | this.emit(REVIVE, this);
57 | }
58 |
59 | return this;
60 | };
61 |
62 | export const setMinHealth = function (amount, silent) {
63 | this.setData(MIN_HEALTH, amount);
64 |
65 | if (this.getHealth() < amount) {
66 | this.setHealth(amount, silent);
67 | }
68 |
69 | return this;
70 | };
71 |
72 | export const setMaxHealth = function (amount, silent) {
73 | this.setData(MAX_HEALTH, amount);
74 |
75 | if (this.getHealth() > amount) {
76 | this.setHealth(amount, silent);
77 | }
78 |
79 | return this;
80 | };
81 |
82 | export const damage = function (amount, silent) {
83 | if (amount === 0) return this;
84 |
85 | if (!amount) amount = 1;
86 |
87 | this.setHealth(this.getHealth() - amount, undefined, undefined, silent);
88 |
89 | return this;
90 | };
91 |
92 | export const heal = function (amount, silent) {
93 | if (amount === 0) return this;
94 |
95 | if (!amount) amount = 1;
96 |
97 | this.setHealth(this.getHealth() + amount, undefined, undefined, silent);
98 |
99 | return this;
100 | };
101 |
102 | export const isAlive = function () {
103 | return this.getHealth() > 0;
104 | };
105 |
106 | export const isDead = function () {
107 | return this.getHealth() <= 0;
108 | };
109 |
110 | export const kill = function (silent) {
111 | if (this.isAlive()) {
112 | this.setHealth(0, silent);
113 | }
114 |
115 | return this;
116 | };
117 |
118 | export const revive = function (health, silent) {
119 | if (this.isDead()) {
120 | this.setHealth(health || 1, silent);
121 | }
122 |
123 | return this;
124 | };
125 |
126 | export const reviveAtMaxHealth = function (silent) {
127 | this.revive(this.getMaxHealth(), silent);
128 |
129 | return this;
130 | };
131 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Phaser 3 Health Component
2 | =========================
3 |
4 | Flexible, data-based health component.
5 |
6 | Examples
7 | --------
8 |
9 | ```javascript
10 | // Add component to *one* game object and assign health=1, minHealth=0, maxHealth=2
11 | PhaserHealth.AddTo(sprite, 1, 0, 2);
12 |
13 | // Same, in two calls:
14 | PhaserHealth.AddTo(sprite).setHealth(1, 0, 2);
15 |
16 | // Add component to Sprite class
17 | PhaserHealth.MixinTo(Phaser.GameObjects.Sprite);
18 | // Then set health values on a game object
19 | var sprite = this.add.sprite(/*…*/).setHealth(1, 0, 2);
20 |
21 | // Hide and deactivate sprite when health decreases below 0
22 | sprite.on('die', function (obj) {
23 | obj.setActive(false).setVisible(false);
24 | });
25 |
26 | // Show and activate sprite when health increases above 0
27 | sprite.on('revive', function (obj) {
28 | obj.setActive(true).setVisible(true);
29 | });
30 |
31 | // React to health changes
32 | sprite.on('healthchange', function (obj, amount, health, minHealth, maxHealth) {
33 | // Health changed by ${amount}, now ${health}/${maxHealth}
34 | });
35 |
36 | // Decrease health by 1
37 | sprite.damage(1);
38 |
39 | // Increase health by 2
40 | sprite.heal(2);
41 |
42 | // Set health to 0, only if health > 0
43 | sprite.kill();
44 |
45 | // Set health to 1, only if health <= 0
46 | sprite.revive();
47 |
48 | // Set health to maximum, only if health <= 0
49 | sprite.reviveAtMaxHealth();
50 | ```
51 |
52 | See the [demo](demo/demo.js) for more example uses.
53 |
54 | Tests
55 | -----
56 |
57 | Open [tests](./tests/index.html) in your browser.
58 |
59 | Add
60 | ---
61 |
62 | ### Browser / UMD
63 |
64 | Include the [UMD script](https://github.com/samme/phaser-component-health/blob/master/dist/phaser-component-health.umd.js) after Phaser and then use the global variable `PhaserHealth`. [The demo](https://codepen.io/samme/pen/BeyZpX) is set up this way.
65 |
66 | ```html
67 |
68 |
69 | ```
70 |
71 | ### Modules
72 |
73 | Use the module's default export:
74 |
75 | ```js
76 | import PhaserHealth from 'phaser-component-health';
77 | ```
78 |
79 | API
80 | ---
81 |
82 | ### Static Methods
83 |
84 | - PhaserHealth.AddTo(obj, health=1, minHealth=-Infinity, maxHealth=100) → obj
85 | - PhaserHealth.Damage(obj, amount=1, silent=false) → obj
86 | - PhaserHealth.Dump(objs) → undefined (see output in console)
87 | - PhaserHealth.Heal(obj, amount=1, silent=false) → obj
88 | - PhaserHealth.Kill(obj, silent=false) → obj
89 | - PhaserHealth.MixinTo(class) → class
90 | - PhaserHealth.Revive(obj, health=1, silent=false) → obj
91 | - PhaserHealth.ReviveAtMaxHealth(obj, silent=false) → obj
92 | - PhaserHealth.SetHealth(obj, health, minHealth, maxHealth, silent=false) → obj
93 |
94 | ### Actions
95 |
96 | - PhaserHealth.Actions.Damage(objs, amount=1, silent=false) → objs
97 | - PhaserHealth.Actions.Heal(objs, amount=1, silent=false) → objs
98 | - PhaserHealth.Actions.Kill(objs, silent=false) → objs
99 | - PhaserHealth.Actions.Revive(objs, health=1, silent=false) → objs
100 | - PhaserHealth.Actions.ReviveAtMaxHealth(objs, silent=false) → objs
101 | - PhaserHealth.Actions.SetHealth(objs, health, minHealth, maxHealth, silent=false) → objs
102 |
103 | ### Component Methods
104 |
105 | - damage(amount=1, silent=false) → this
106 | - getHealth() → number
107 | - getHealthFrac() → number
108 | - getMaxHealth() → number
109 | - getMinHealth() → number
110 | - heal(amount=1, silent=false) → this
111 | - isAlive() → boolean
112 | - isDead() → boolean
113 | - kill(silent=false) → this
114 | - revive(health=1, silent=false) → this
115 | - reviveAtMaxHealth(silent=false) → this
116 | - setHealth(health, minHealth, maxHealth, silent=false) → this
117 | - setMaxHealth(maxHealth, silent=false) → this
118 | - setMinHealth(minHealth, silent=false) → this
119 |
120 | ### Events
121 |
122 | 1. `healthchange` → (obj, amount, health, minHealth, maxHealth)
123 | 2. `damage`, `heal` → (obj, amount)
124 | 3. `die`, `revive` → (obj)
125 |
126 | These are also named as
127 |
128 | - PhaserHealth.Events.DAMAGE
129 | - PhaserHealth.Events.DIE
130 | - PhaserHealth.Events.HEAL
131 | - PhaserHealth.Events.HEALTH_CHANGE
132 | - PhaserHealth.Events.REVIVE
133 |
--------------------------------------------------------------------------------
/dist/phaser-component-health.esm.js:
--------------------------------------------------------------------------------
1 | import Phaser from 'phaser';
2 |
3 | var DAMAGE = 'damage';
4 | var DIE = 'die';
5 | var HEAL = 'heal';
6 | var HEALTH = 'health';
7 | var HEALTH_CHANGE = 'healthchange';
8 | var MAX_HEALTH = 'maxHealth';
9 | var MIN_HEALTH = 'minHealth';
10 | var REVIVE = 'revive';
11 |
12 | var ref = Phaser.Math;
13 | var Clamp = ref.Clamp;
14 |
15 | var getHealth = function () {
16 | return this.getData(HEALTH);
17 | };
18 |
19 | var getHealthFrac = function () {
20 | return this.getHealth() / this.getMaxHealth();
21 | };
22 |
23 | var getMinHealth = function () {
24 | return this.getData(MIN_HEALTH);
25 | };
26 |
27 | var getMaxHealth = function () {
28 | return this.getData(MAX_HEALTH);
29 | };
30 |
31 | var setHealth = function (health, minHealth, maxHealth, silent) {
32 | if (minHealth !== undefined) {
33 | this.setMinHealth(minHealth, true);
34 | }
35 |
36 | if (maxHealth !== undefined) {
37 | this.setMaxHealth(maxHealth, true);
38 | }
39 |
40 | minHealth = this.getMinHealth();
41 | maxHealth = this.getMaxHealth();
42 |
43 | var prevHealth = this.getHealth();
44 | var newHealth = Clamp(health, minHealth, maxHealth);
45 | var change = newHealth - prevHealth;
46 |
47 | if (change === 0) { return this; }
48 |
49 | this.setData(HEALTH, newHealth);
50 |
51 | if (silent) { return this; }
52 |
53 | this.emit(HEALTH_CHANGE, this, change, newHealth, minHealth, maxHealth);
54 |
55 | if (change > 0) {
56 | this.emit(HEAL, this, change);
57 | } else {
58 | this.emit(DAMAGE, this, -change);
59 | }
60 |
61 | if (prevHealth > 0 && newHealth <= 0) {
62 | this.emit(DIE, this);
63 | } else if (prevHealth <= 0 && newHealth > 0) {
64 | this.emit(REVIVE, this);
65 | }
66 |
67 | return this;
68 | };
69 |
70 | var setMinHealth = function (amount, silent) {
71 | this.setData(MIN_HEALTH, amount);
72 |
73 | if (this.getHealth() < amount) {
74 | this.setHealth(amount, silent);
75 | }
76 |
77 | return this;
78 | };
79 |
80 | var setMaxHealth = function (amount, silent) {
81 | this.setData(MAX_HEALTH, amount);
82 |
83 | if (this.getHealth() > amount) {
84 | this.setHealth(amount, silent);
85 | }
86 |
87 | return this;
88 | };
89 |
90 | var damage = function (amount, silent) {
91 | if (amount === 0) { return this; }
92 |
93 | if (!amount) { amount = 1; }
94 |
95 | this.setHealth(this.getHealth() - amount, undefined, undefined, silent);
96 |
97 | return this;
98 | };
99 |
100 | var heal = function (amount, silent) {
101 | if (amount === 0) { return this; }
102 |
103 | if (!amount) { amount = 1; }
104 |
105 | this.setHealth(this.getHealth() + amount, undefined, undefined, silent);
106 |
107 | return this;
108 | };
109 |
110 | var isAlive = function () {
111 | return this.getHealth() > 0;
112 | };
113 |
114 | var isDead = function () {
115 | return this.getHealth() <= 0;
116 | };
117 |
118 | var kill = function (silent) {
119 | if (this.isAlive()) {
120 | this.setHealth(0, silent);
121 | }
122 |
123 | return this;
124 | };
125 |
126 | var revive = function (health, silent) {
127 | if (this.isDead()) {
128 | this.setHealth(health || 1, silent);
129 | }
130 |
131 | return this;
132 | };
133 |
134 | var reviveAtMaxHealth = function (silent) {
135 | this.revive(this.getMaxHealth(), silent);
136 |
137 | return this;
138 | };
139 |
140 | var HealthComponent = /*#__PURE__*/Object.freeze({
141 | __proto__: null,
142 | getHealth: getHealth,
143 | getHealthFrac: getHealthFrac,
144 | getMinHealth: getMinHealth,
145 | getMaxHealth: getMaxHealth,
146 | setHealth: setHealth,
147 | setMinHealth: setMinHealth,
148 | setMaxHealth: setMaxHealth,
149 | damage: damage,
150 | heal: heal,
151 | isAlive: isAlive,
152 | isDead: isDead,
153 | kill: kill,
154 | revive: revive,
155 | reviveAtMaxHealth: reviveAtMaxHealth
156 | });
157 |
158 | var dumpMap = function (obj) {
159 | return {
160 | name: obj.name,
161 | alive: obj.isAlive(),
162 | health: obj.getHealth(),
163 | minHealth: obj.getMinHealth(),
164 | maxHealth: obj.getMaxHealth()
165 | };
166 | };
167 |
168 | var AddTo = function (obj, health, minHealth, maxHealth) {
169 | if ( health === void 0 ) health = 1;
170 | if ( minHealth === void 0 ) minHealth = -Infinity;
171 | if ( maxHealth === void 0 ) maxHealth = 100;
172 |
173 | Object.assign(obj, HealthComponent);
174 |
175 | SetHealth(obj, health, minHealth, maxHealth, true);
176 |
177 | return obj;
178 | };
179 |
180 | var Damage = function (obj, amount, silent) {
181 | return obj.damage(amount, silent);
182 | };
183 |
184 | var Dump = function (objs) {
185 | console.table(objs.map(dumpMap));
186 | };
187 |
188 | var Heal = function (obj, amount, silent) {
189 | return obj.heal(amount, silent);
190 | };
191 |
192 | var IsAlive = function (obj) {
193 | return obj.isAlive();
194 | };
195 |
196 | var IsDead = function (obj) {
197 | return obj.isDead();
198 | };
199 |
200 | var Kill = function (obj, silent) {
201 | return obj.kill(silent);
202 | };
203 |
204 | var MixinTo = function (obj) {
205 | return Object.assign(obj.prototype, HealthComponent);
206 | };
207 |
208 | var Revive = function (obj, health, silent) {
209 | return obj.revive(health, silent);
210 | };
211 |
212 | var ReviveAtMaxHealth = function (obj, silent) {
213 | return obj.reviveAtMaxHealth(silent);
214 | };
215 |
216 | var SetHealth = function (obj, health, minHealth, maxHealth, silent) {
217 | return obj.setHealth(health, minHealth, maxHealth, silent);
218 | };
219 |
220 | var Health = /*#__PURE__*/Object.freeze({
221 | __proto__: null,
222 | AddTo: AddTo,
223 | Damage: Damage,
224 | Dump: Dump,
225 | Heal: Heal,
226 | IsAlive: IsAlive,
227 | IsDead: IsDead,
228 | Kill: Kill,
229 | MixinTo: MixinTo,
230 | Revive: Revive,
231 | ReviveAtMaxHealth: ReviveAtMaxHealth,
232 | SetHealth: SetHealth
233 | });
234 |
235 | var ref$1 = Phaser.Utils.Array;
236 | var Each = ref$1.Each;
237 |
238 | var Damage$1 = function (objs, amount, silent) {
239 | Each(objs, Damage, null, amount, silent);
240 |
241 | return objs;
242 | };
243 |
244 | var Heal$1 = function (objs, amount, silent) {
245 | Each(objs, Heal, null, amount, silent);
246 |
247 | return objs;
248 | };
249 |
250 | var Kill$1 = function (objs, silent) {
251 | Each(objs, Kill, null, silent);
252 |
253 | return objs;
254 | };
255 |
256 | var Revive$1 = function (objs, health, silent) {
257 | Each(objs, Revive, null, health, silent);
258 |
259 | return objs;
260 | };
261 |
262 | var ReviveAtMaxHealth$1 = function (objs, silent) {
263 | Each(objs, ReviveAtMaxHealth, null, silent);
264 |
265 | return objs;
266 | };
267 |
268 | var SetHealth$1 = function (objs, health, maxHealth, silent) {
269 | Each(objs, SetHealth, null, health, maxHealth, silent);
270 |
271 | return objs;
272 | };
273 |
274 | var Actions = /*#__PURE__*/Object.freeze({
275 | __proto__: null,
276 | Damage: Damage$1,
277 | Heal: Heal$1,
278 | Kill: Kill$1,
279 | Revive: Revive$1,
280 | ReviveAtMaxHealth: ReviveAtMaxHealth$1,
281 | SetHealth: SetHealth$1
282 | });
283 |
284 |
285 |
286 | var Events = /*#__PURE__*/Object.freeze({
287 | __proto__: null,
288 | DAMAGE: DAMAGE,
289 | DIE: DIE,
290 | HEAL: HEAL,
291 | HEALTH_CHANGE: HEALTH_CHANGE,
292 | REVIVE: REVIVE
293 | });
294 |
295 | var main = Object.assign({}, Health,
296 | {Actions: Actions,
297 | Events: Events,
298 | HealthComponent: HealthComponent});
299 |
300 | export default main;
301 |
--------------------------------------------------------------------------------
/dist/phaser-component-health.umd.js:
--------------------------------------------------------------------------------
1 | (function (global, factory) {
2 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('phaser')) :
3 | typeof define === 'function' && define.amd ? define(['phaser'], factory) :
4 | (global = global || self, global.PhaserHealth = factory(global.Phaser));
5 | }(this, (function (Phaser) { 'use strict';
6 |
7 | Phaser = Phaser && Phaser.hasOwnProperty('default') ? Phaser['default'] : Phaser;
8 |
9 | var DAMAGE = 'damage';
10 | var DIE = 'die';
11 | var HEAL = 'heal';
12 | var HEALTH = 'health';
13 | var HEALTH_CHANGE = 'healthchange';
14 | var MAX_HEALTH = 'maxHealth';
15 | var MIN_HEALTH = 'minHealth';
16 | var REVIVE = 'revive';
17 |
18 | var ref = Phaser.Math;
19 | var Clamp = ref.Clamp;
20 |
21 | var getHealth = function () {
22 | return this.getData(HEALTH);
23 | };
24 |
25 | var getHealthFrac = function () {
26 | return this.getHealth() / this.getMaxHealth();
27 | };
28 |
29 | var getMinHealth = function () {
30 | return this.getData(MIN_HEALTH);
31 | };
32 |
33 | var getMaxHealth = function () {
34 | return this.getData(MAX_HEALTH);
35 | };
36 |
37 | var setHealth = function (health, minHealth, maxHealth, silent) {
38 | if (minHealth !== undefined) {
39 | this.setMinHealth(minHealth, true);
40 | }
41 |
42 | if (maxHealth !== undefined) {
43 | this.setMaxHealth(maxHealth, true);
44 | }
45 |
46 | minHealth = this.getMinHealth();
47 | maxHealth = this.getMaxHealth();
48 |
49 | var prevHealth = this.getHealth();
50 | var newHealth = Clamp(health, minHealth, maxHealth);
51 | var change = newHealth - prevHealth;
52 |
53 | if (change === 0) { return this; }
54 |
55 | this.setData(HEALTH, newHealth);
56 |
57 | if (silent) { return this; }
58 |
59 | this.emit(HEALTH_CHANGE, this, change, newHealth, minHealth, maxHealth);
60 |
61 | if (change > 0) {
62 | this.emit(HEAL, this, change);
63 | } else {
64 | this.emit(DAMAGE, this, -change);
65 | }
66 |
67 | if (prevHealth > 0 && newHealth <= 0) {
68 | this.emit(DIE, this);
69 | } else if (prevHealth <= 0 && newHealth > 0) {
70 | this.emit(REVIVE, this);
71 | }
72 |
73 | return this;
74 | };
75 |
76 | var setMinHealth = function (amount, silent) {
77 | this.setData(MIN_HEALTH, amount);
78 |
79 | if (this.getHealth() < amount) {
80 | this.setHealth(amount, silent);
81 | }
82 |
83 | return this;
84 | };
85 |
86 | var setMaxHealth = function (amount, silent) {
87 | this.setData(MAX_HEALTH, amount);
88 |
89 | if (this.getHealth() > amount) {
90 | this.setHealth(amount, silent);
91 | }
92 |
93 | return this;
94 | };
95 |
96 | var damage = function (amount, silent) {
97 | if (amount === 0) { return this; }
98 |
99 | if (!amount) { amount = 1; }
100 |
101 | this.setHealth(this.getHealth() - amount, undefined, undefined, silent);
102 |
103 | return this;
104 | };
105 |
106 | var heal = function (amount, silent) {
107 | if (amount === 0) { return this; }
108 |
109 | if (!amount) { amount = 1; }
110 |
111 | this.setHealth(this.getHealth() + amount, undefined, undefined, silent);
112 |
113 | return this;
114 | };
115 |
116 | var isAlive = function () {
117 | return this.getHealth() > 0;
118 | };
119 |
120 | var isDead = function () {
121 | return this.getHealth() <= 0;
122 | };
123 |
124 | var kill = function (silent) {
125 | if (this.isAlive()) {
126 | this.setHealth(0, silent);
127 | }
128 |
129 | return this;
130 | };
131 |
132 | var revive = function (health, silent) {
133 | if (this.isDead()) {
134 | this.setHealth(health || 1, silent);
135 | }
136 |
137 | return this;
138 | };
139 |
140 | var reviveAtMaxHealth = function (silent) {
141 | this.revive(this.getMaxHealth(), silent);
142 |
143 | return this;
144 | };
145 |
146 | var HealthComponent = /*#__PURE__*/Object.freeze({
147 | __proto__: null,
148 | getHealth: getHealth,
149 | getHealthFrac: getHealthFrac,
150 | getMinHealth: getMinHealth,
151 | getMaxHealth: getMaxHealth,
152 | setHealth: setHealth,
153 | setMinHealth: setMinHealth,
154 | setMaxHealth: setMaxHealth,
155 | damage: damage,
156 | heal: heal,
157 | isAlive: isAlive,
158 | isDead: isDead,
159 | kill: kill,
160 | revive: revive,
161 | reviveAtMaxHealth: reviveAtMaxHealth
162 | });
163 |
164 | var dumpMap = function (obj) {
165 | return {
166 | name: obj.name,
167 | alive: obj.isAlive(),
168 | health: obj.getHealth(),
169 | minHealth: obj.getMinHealth(),
170 | maxHealth: obj.getMaxHealth()
171 | };
172 | };
173 |
174 | var AddTo = function (obj, health, minHealth, maxHealth) {
175 | if ( health === void 0 ) health = 1;
176 | if ( minHealth === void 0 ) minHealth = -Infinity;
177 | if ( maxHealth === void 0 ) maxHealth = 100;
178 |
179 | Object.assign(obj, HealthComponent);
180 |
181 | SetHealth(obj, health, minHealth, maxHealth, true);
182 |
183 | return obj;
184 | };
185 |
186 | var Damage = function (obj, amount, silent) {
187 | return obj.damage(amount, silent);
188 | };
189 |
190 | var Dump = function (objs) {
191 | console.table(objs.map(dumpMap));
192 | };
193 |
194 | var Heal = function (obj, amount, silent) {
195 | return obj.heal(amount, silent);
196 | };
197 |
198 | var IsAlive = function (obj) {
199 | return obj.isAlive();
200 | };
201 |
202 | var IsDead = function (obj) {
203 | return obj.isDead();
204 | };
205 |
206 | var Kill = function (obj, silent) {
207 | return obj.kill(silent);
208 | };
209 |
210 | var MixinTo = function (obj) {
211 | return Object.assign(obj.prototype, HealthComponent);
212 | };
213 |
214 | var Revive = function (obj, health, silent) {
215 | return obj.revive(health, silent);
216 | };
217 |
218 | var ReviveAtMaxHealth = function (obj, silent) {
219 | return obj.reviveAtMaxHealth(silent);
220 | };
221 |
222 | var SetHealth = function (obj, health, minHealth, maxHealth, silent) {
223 | return obj.setHealth(health, minHealth, maxHealth, silent);
224 | };
225 |
226 | var Health = /*#__PURE__*/Object.freeze({
227 | __proto__: null,
228 | AddTo: AddTo,
229 | Damage: Damage,
230 | Dump: Dump,
231 | Heal: Heal,
232 | IsAlive: IsAlive,
233 | IsDead: IsDead,
234 | Kill: Kill,
235 | MixinTo: MixinTo,
236 | Revive: Revive,
237 | ReviveAtMaxHealth: ReviveAtMaxHealth,
238 | SetHealth: SetHealth
239 | });
240 |
241 | var ref$1 = Phaser.Utils.Array;
242 | var Each = ref$1.Each;
243 |
244 | var Damage$1 = function (objs, amount, silent) {
245 | Each(objs, Damage, null, amount, silent);
246 |
247 | return objs;
248 | };
249 |
250 | var Heal$1 = function (objs, amount, silent) {
251 | Each(objs, Heal, null, amount, silent);
252 |
253 | return objs;
254 | };
255 |
256 | var Kill$1 = function (objs, silent) {
257 | Each(objs, Kill, null, silent);
258 |
259 | return objs;
260 | };
261 |
262 | var Revive$1 = function (objs, health, silent) {
263 | Each(objs, Revive, null, health, silent);
264 |
265 | return objs;
266 | };
267 |
268 | var ReviveAtMaxHealth$1 = function (objs, silent) {
269 | Each(objs, ReviveAtMaxHealth, null, silent);
270 |
271 | return objs;
272 | };
273 |
274 | var SetHealth$1 = function (objs, health, maxHealth, silent) {
275 | Each(objs, SetHealth, null, health, maxHealth, silent);
276 |
277 | return objs;
278 | };
279 |
280 | var Actions = /*#__PURE__*/Object.freeze({
281 | __proto__: null,
282 | Damage: Damage$1,
283 | Heal: Heal$1,
284 | Kill: Kill$1,
285 | Revive: Revive$1,
286 | ReviveAtMaxHealth: ReviveAtMaxHealth$1,
287 | SetHealth: SetHealth$1
288 | });
289 |
290 |
291 |
292 | var Events = /*#__PURE__*/Object.freeze({
293 | __proto__: null,
294 | DAMAGE: DAMAGE,
295 | DIE: DIE,
296 | HEAL: HEAL,
297 | HEALTH_CHANGE: HEALTH_CHANGE,
298 | REVIVE: REVIVE
299 | });
300 |
301 | var main = Object.assign({}, Health,
302 | {Actions: Actions,
303 | Events: Events,
304 | HealthComponent: HealthComponent});
305 |
306 | return main;
307 |
308 | })));
309 |
--------------------------------------------------------------------------------
/demo/demo.js:
--------------------------------------------------------------------------------
1 | /* global Phaser, PhaserHealth */
2 | var BLUE = 0x00ffff;
3 | var GREEN = 0x22ff00;
4 | var RED = 0xff2200;
5 | var Health = PhaserHealth;
6 | var Shuffle = Phaser.Utils.Array.Shuffle;
7 |
8 | class HealthBar {
9 | constructor (scene, x, y, width, height) {
10 | this.bg = scene.add.rectangle(x, y, width + 2, height + 2, 0, 0.6)
11 | .setOrigin(0, 0);
12 |
13 | this.bar = scene.add.rectangle(x + 1, y + 1, width, height, GREEN)
14 | .setOrigin(0, 0);
15 | }
16 |
17 | add (target) {
18 | target.on('healthchange', this.draw, this);
19 | }
20 |
21 | draw (target, change, health, max) {
22 | this.bar.displayWidth = Math.max(0, health);
23 | }
24 | }
25 |
26 | class Missile extends Phaser.GameObjects.Image {
27 | constructor (scene, frame) {
28 | super(scene, 0, 0, 'elves', frame);
29 |
30 | this.visible = false;
31 | }
32 | }
33 |
34 | class Elf extends Phaser.GameObjects.Sprite {
35 | constructor (scene, color, x, y) {
36 | super(scene, x, y, 'elves');
37 |
38 | this.centerX = (color === 'blue')
39 | ? (x - 0.25 * this.width)
40 | : (x + 0.25 * this.width);
41 |
42 | this.color = color;
43 |
44 | this.play(this.color + 'Idle');
45 |
46 | scene.add.existing(this);
47 |
48 | this.on('animationcomplete', this.animComplete, this);
49 |
50 | // (health, minHealth, maxHealth)
51 | this.setHealth(50, 0, 50);
52 |
53 | this.on('die', this.onDie, this);
54 | this.on('revive', this.onRevive, this);
55 |
56 | this.healthBar = new HealthBar(
57 | scene,
58 | this.centerX - 25,
59 | this.y + 75,
60 | this.getMaxHealth(), 20
61 | );
62 | this.healthBar.add(this);
63 |
64 | this.timer = null;
65 |
66 | this.scheduleFire();
67 |
68 | this.setInteractive(new Phaser.Geom.Rectangle(
69 | (this.color === 'blue') ? 0 : (0.6 * this.width),
70 | 0,
71 | 0.4 * this.width,
72 | this.height
73 | ), Phaser.Geom.Rectangle.Contains);
74 |
75 | this.on('pointerdown', this.onPointerDown, this);
76 | }
77 |
78 | animComplete (animation) {
79 | if (animation.key === (this.color + 'Attack')) {
80 | this.play(this.color + 'Idle');
81 | }
82 | }
83 |
84 | scheduleFire () {
85 | this.timer = this.scene.time.addEvent({
86 | delay: Phaser.Math.Between(1000, 3000),
87 | callback: this.fire,
88 | callbackScope: this
89 | });
90 | }
91 |
92 | fire () {
93 | if (!this.isAlive()) return;
94 |
95 | this.scheduleFire();
96 |
97 | var target = (this.color === 'blue') ? getGreen() : getBlue();
98 |
99 | if (target) {
100 | this.play(this.color + 'Attack');
101 |
102 | var offset = (this.color === 'blue') ? 20 : -20;
103 | var targetX = (this.color === 'blue') ? target.x + 30 : target.x - 30;
104 |
105 | this.missile.setPosition(this.x + offset, this.y + 20).setVisible(true);
106 |
107 | this.scene.tweens.add({
108 | targets: this.missile,
109 | x: targetX,
110 | ease: 'Linear',
111 | duration: Math.abs(this.x - target.x),
112 | onComplete: function (tween, targets) {
113 | targets[0].setVisible(false);
114 | target.damage(Phaser.Math.Between(1, 9));
115 | }
116 | });
117 | }
118 | }
119 |
120 | onDie () {
121 | this.play(this.color + 'Dead');
122 | }
123 |
124 | onRevive () {
125 | this.play(this.color + 'Idle');
126 |
127 | this.scheduleFire();
128 | }
129 |
130 | onPointerDown () {
131 | this.heal(10);
132 | }
133 | }
134 |
135 | class BlueElf extends Elf {
136 | constructor (scene, x, y) {
137 | super(scene, 'blue', x, y);
138 |
139 | this.missile = new Missile(scene, 'blue-missile');
140 |
141 | scene.add.existing(this.missile);
142 | }
143 | }
144 |
145 | class GreenElf extends Elf {
146 | constructor (scene, x, y) {
147 | super(scene, 'green', x, y);
148 |
149 | this.missile = new Missile(scene, 'green-missile');
150 |
151 | scene.add.existing(this.missile);
152 | }
153 | }
154 |
155 | Health.MixinTo(Elf);
156 |
157 | var Counter = {
158 | blue: 0,
159 |
160 | green: 0,
161 |
162 | text: null,
163 |
164 | add: function (elf) {
165 | elf.on('die', this.countDeath, this);
166 | elf.on('revive', this.countRevive, this);
167 | },
168 |
169 | countDeath: function (elf) {
170 | this[elf.color]--;
171 |
172 | this.drawCount();
173 | },
174 |
175 | countRevive: function (elf) {
176 | this[elf.color]++;
177 |
178 | this.drawCount();
179 | },
180 |
181 | drawCount: function () {
182 | this.text.setText(`Blue: ${this.blue} Green: ${this.green}`);
183 | }
184 | };
185 |
186 | var Logger = {
187 | add: function (elf) {
188 | elf.on('healthchange', this.logHealthChange, this);
189 | elf.on('die', this.logDeath, this);
190 | elf.on('revive', this.logRevive, this);
191 | elf.on('damage', this.logDamage, this);
192 | elf.on('heal', this.logHeal, this);
193 | },
194 |
195 | logHealthChange: function (elf, amount) {
196 | console.log('healthchange', elf.name, amount);
197 | },
198 |
199 | logDeath: function (elf) {
200 | console.log('die', elf.name);
201 | },
202 |
203 | logRevive: function (elf) {
204 | console.log('revive', elf.name);
205 | },
206 |
207 | logDamage: function (elf, amount) {
208 | console.log('damage', elf.name, amount);
209 | },
210 |
211 | logHeal: function (elf, amount) {
212 | console.log('heal', elf.name, amount);
213 | }
214 | };
215 |
216 | var Texter = {
217 | text: null,
218 |
219 | add: function (elf) {
220 | elf.on('healthchange', this.onHealthChange, this);
221 | },
222 |
223 | onHealthChange: function (elf, change) {
224 | this.text
225 | .setPosition(elf.centerX, elf.y - 100)
226 | .setText(change)
227 | .setOrigin(0.5, 0.5);
228 | }
229 | };
230 |
231 | var Tinter = {
232 | add: function (elf) {
233 | elf.on('damage', this.tintDamage, this);
234 | elf.on('heal', this.tintHeal, this);
235 | },
236 |
237 | tintDamage: function (elf) {
238 | elf.setTint(RED);
239 | this.scheduleClearTint(elf);
240 | },
241 |
242 | tintHeal: function (elf) {
243 | elf.setTint(BLUE);
244 | this.scheduleClearTint(elf);
245 | },
246 |
247 | scheduleClearTint: function (elf) {
248 | elf.scene.time.addEvent({ delay: 200, callback: elf.clearTint, callbackScope: elf });
249 | }
250 | };
251 |
252 | var config = {
253 | width: 1024,
254 | height: 600,
255 | pixelArt: true,
256 | scene: {
257 | preload: preload,
258 | create: create
259 | }
260 | };
261 |
262 | var blues = [];
263 | var greens = [];
264 |
265 | // eslint-disable-next-line no-new
266 | new Phaser.Game(config);
267 |
268 | function preload () {
269 | // The graphics used in this example were free downloads from https://craftpix.net
270 | // Check out their excellent asset packs!
271 | this.load.image('background', 'assets/pics/fairy-background-craft-pixel.png');
272 | this.load.atlas('elves', 'assets/animations/elves-craft-pixel.png', 'assets/animations/elves-craft-pixel.json');
273 | }
274 |
275 | function create () {
276 | this.anims.create({ key: 'greenIdle', frames: this.anims.generateFrameNames('elves', { prefix: 'green_idle_', start: 0, end: 4 }), frameRate: 10, repeat: -1 });
277 | this.anims.create({ key: 'blueIdle', frames: this.anims.generateFrameNames('elves', { prefix: 'blue_idle_', start: 0, end: 4 }), frameRate: 10, repeat: -1 });
278 |
279 | this.anims.create({ key: 'greenAttack', frames: this.anims.generateFrameNames('elves', { prefix: 'green_attack_', start: 0, end: 5 }), frameRate: 10 });
280 | this.anims.create({ key: 'blueAttack', frames: this.anims.generateFrameNames('elves', { prefix: 'blue_attack_', start: 0, end: 4 }), frameRate: 10 });
281 |
282 | this.anims.create({ key: 'greenDead', frames: this.anims.generateFrameNames('elves', { prefix: 'green_die_', start: 0, end: 4 }), frameRate: 6 });
283 | this.anims.create({ key: 'blueDead', frames: this.anims.generateFrameNames('elves', { prefix: 'blue_die_', start: 0, end: 4 }), frameRate: 6 });
284 |
285 | this.add.image(0, 0, 'background').setOrigin(0).setAlpha(0.5);
286 |
287 | Counter.text = this.add.text(10, 10, '', { font: '24px sans-serif' });
288 |
289 | Texter.text = this.add.text(0, 0, '', { font: '48px sans-serif' });
290 |
291 | blues.push(new BlueElf(this, 120, 476).setName('Onas'));
292 | blues.push(new BlueElf(this, 220, 480).setName('Aiduin'));
293 | blues.push(new BlueElf(this, 320, 484).setName('Kivessin'));
294 | blues.push(new BlueElf(this, 440, 480).setName('Respen'));
295 |
296 | greens.push(new GreenElf(this, 560, 486).setName('Hagduin'));
297 | greens.push(new GreenElf(this, 670, 488).setName('Alre'));
298 | greens.push(new GreenElf(this, 780, 485).setName('Akkar'));
299 | greens.push(new GreenElf(this, 890, 484).setName('Riluaneth'));
300 |
301 | Counter.blue = blues.length;
302 | Counter.green = greens.length;
303 | Counter.drawCount();
304 |
305 | blues.forEach(addElf);
306 | greens.forEach(addElf);
307 |
308 | console.info('Blue Elves:');
309 |
310 | Health.Dump(blues);
311 |
312 | console.info('Green Elves:');
313 |
314 | Health.Dump(greens);
315 | }
316 |
317 | function addElf (elf) {
318 | Counter.add(elf);
319 | Logger.add(elf);
320 | Texter.add(elf);
321 | Tinter.add(elf);
322 | }
323 |
324 | function getRandomAlive (objs) {
325 | return Shuffle(objs.filter(Health.IsAlive))[0] || null;
326 | }
327 |
328 | function getGreen () {
329 | return getRandomAlive(greens);
330 | }
331 |
332 | function getBlue () {
333 | return getRandomAlive(blues);
334 | }
335 |
--------------------------------------------------------------------------------
/demo/assets/animations/elves-craft-pixel.json:
--------------------------------------------------------------------------------
1 | {
2 | "textures": [
3 | {
4 | "image": "elves-craft-pixel.png",
5 | "format": "RGBA8888",
6 | "size": {
7 | "w": 494,
8 | "h": 1014
9 | },
10 | "scale": 1,
11 | "frames": [
12 | {
13 | "filename": "green_attack_4",
14 | "rotated": false,
15 | "trimmed": true,
16 | "sourceSize": {
17 | "w": 265,
18 | "h": 134
19 | },
20 | "spriteSourceSize": {
21 | "x": 0,
22 | "y": 0,
23 | "w": 262,
24 | "h": 133
25 | },
26 | "frame": {
27 | "x": 2,
28 | "y": 2,
29 | "w": 262,
30 | "h": 133
31 | }
32 | },
33 | {
34 | "filename": "blue_attack_3",
35 | "rotated": false,
36 | "trimmed": true,
37 | "sourceSize": {
38 | "w": 235,
39 | "h": 138
40 | },
41 | "spriteSourceSize": {
42 | "x": 1,
43 | "y": 3,
44 | "w": 195,
45 | "h": 134
46 | },
47 | "frame": {
48 | "x": 266,
49 | "y": 2,
50 | "w": 195,
51 | "h": 134
52 | }
53 | },
54 | {
55 | "filename": "blue_attack_4",
56 | "rotated": false,
57 | "trimmed": true,
58 | "sourceSize": {
59 | "w": 235,
60 | "h": 138
61 | },
62 | "spriteSourceSize": {
63 | "x": 1,
64 | "y": 3,
65 | "w": 234,
66 | "h": 134
67 | },
68 | "frame": {
69 | "x": 2,
70 | "y": 137,
71 | "w": 234,
72 | "h": 134
73 | }
74 | },
75 | {
76 | "filename": "green_attack_3",
77 | "rotated": false,
78 | "trimmed": true,
79 | "sourceSize": {
80 | "w": 265,
81 | "h": 134
82 | },
83 | "spriteSourceSize": {
84 | "x": 70,
85 | "y": 0,
86 | "w": 194,
87 | "h": 133
88 | },
89 | "frame": {
90 | "x": 238,
91 | "y": 138,
92 | "w": 194,
93 | "h": 133
94 | }
95 | },
96 | {
97 | "filename": "blue-missile",
98 | "rotated": false,
99 | "trimmed": true,
100 | "sourceSize": {
101 | "w": 32,
102 | "h": 32
103 | },
104 | "spriteSourceSize": {
105 | "x": 1,
106 | "y": 2,
107 | "w": 30,
108 | "h": 27
109 | },
110 | "frame": {
111 | "x": 434,
112 | "y": 138,
113 | "w": 30,
114 | "h": 27
115 | }
116 | },
117 | {
118 | "filename": "green_attack_2",
119 | "rotated": false,
120 | "trimmed": true,
121 | "sourceSize": {
122 | "w": 265,
123 | "h": 134
124 | },
125 | "spriteSourceSize": {
126 | "x": 134,
127 | "y": 1,
128 | "w": 131,
129 | "h": 133
130 | },
131 | "frame": {
132 | "x": 2,
133 | "y": 273,
134 | "w": 131,
135 | "h": 133
136 | }
137 | },
138 | {
139 | "filename": "blue_attack_2",
140 | "rotated": false,
141 | "trimmed": true,
142 | "sourceSize": {
143 | "w": 235,
144 | "h": 138
145 | },
146 | "spriteSourceSize": {
147 | "x": 1,
148 | "y": 2,
149 | "w": 123,
150 | "h": 134
151 | },
152 | "frame": {
153 | "x": 135,
154 | "y": 273,
155 | "w": 123,
156 | "h": 134
157 | }
158 | },
159 | {
160 | "filename": "green_attack_1",
161 | "rotated": false,
162 | "trimmed": true,
163 | "sourceSize": {
164 | "w": 265,
165 | "h": 134
166 | },
167 | "spriteSourceSize": {
168 | "x": 143,
169 | "y": 1,
170 | "w": 122,
171 | "h": 133
172 | },
173 | "frame": {
174 | "x": 2,
175 | "y": 408,
176 | "w": 122,
177 | "h": 133
178 | }
179 | },
180 | {
181 | "filename": "green_idle_2",
182 | "rotated": false,
183 | "trimmed": true,
184 | "sourceSize": {
185 | "w": 265,
186 | "h": 134
187 | },
188 | "spriteSourceSize": {
189 | "x": 145,
190 | "y": 2,
191 | "w": 117,
192 | "h": 130
193 | },
194 | "frame": {
195 | "x": 260,
196 | "y": 273,
197 | "w": 117,
198 | "h": 130
199 | }
200 | },
201 | {
202 | "filename": "green_idle_3",
203 | "rotated": false,
204 | "trimmed": true,
205 | "sourceSize": {
206 | "w": 265,
207 | "h": 134
208 | },
209 | "spriteSourceSize": {
210 | "x": 145,
211 | "y": 2,
212 | "w": 117,
213 | "h": 130
214 | },
215 | "frame": {
216 | "x": 260,
217 | "y": 273,
218 | "w": 117,
219 | "h": 130
220 | }
221 | },
222 | {
223 | "filename": "blue_attack_0",
224 | "rotated": false,
225 | "trimmed": true,
226 | "sourceSize": {
227 | "w": 235,
228 | "h": 138
229 | },
230 | "spriteSourceSize": {
231 | "x": 1,
232 | "y": 3,
233 | "w": 107,
234 | "h": 134
235 | },
236 | "frame": {
237 | "x": 379,
238 | "y": 273,
239 | "w": 107,
240 | "h": 134
241 | }
242 | },
243 | {
244 | "filename": "green_idle_1",
245 | "rotated": false,
246 | "trimmed": true,
247 | "sourceSize": {
248 | "w": 265,
249 | "h": 134
250 | },
251 | "spriteSourceSize": {
252 | "x": 146,
253 | "y": 1,
254 | "w": 116,
255 | "h": 131
256 | },
257 | "frame": {
258 | "x": 260,
259 | "y": 405,
260 | "w": 116,
261 | "h": 131
262 | }
263 | },
264 | {
265 | "filename": "green_idle_4",
266 | "rotated": false,
267 | "trimmed": true,
268 | "sourceSize": {
269 | "w": 265,
270 | "h": 134
271 | },
272 | "spriteSourceSize": {
273 | "x": 146,
274 | "y": 1,
275 | "w": 116,
276 | "h": 131
277 | },
278 | "frame": {
279 | "x": 260,
280 | "y": 405,
281 | "w": 116,
282 | "h": 131
283 | }
284 | },
285 | {
286 | "filename": "green_die_0",
287 | "rotated": false,
288 | "trimmed": true,
289 | "sourceSize": {
290 | "w": 265,
291 | "h": 134
292 | },
293 | "spriteSourceSize": {
294 | "x": 147,
295 | "y": 1,
296 | "w": 114,
297 | "h": 132
298 | },
299 | "frame": {
300 | "x": 378,
301 | "y": 409,
302 | "w": 114,
303 | "h": 132
304 | }
305 | },
306 | {
307 | "filename": "green_attack_0",
308 | "rotated": false,
309 | "trimmed": true,
310 | "sourceSize": {
311 | "w": 265,
312 | "h": 134
313 | },
314 | "spriteSourceSize": {
315 | "x": 146,
316 | "y": 1,
317 | "w": 115,
318 | "h": 131
319 | },
320 | "frame": {
321 | "x": 126,
322 | "y": 409,
323 | "w": 115,
324 | "h": 131
325 | }
326 | },
327 | {
328 | "filename": "green_attack_5",
329 | "rotated": false,
330 | "trimmed": true,
331 | "sourceSize": {
332 | "w": 265,
333 | "h": 134
334 | },
335 | "spriteSourceSize": {
336 | "x": 146,
337 | "y": 1,
338 | "w": 115,
339 | "h": 131
340 | },
341 | "frame": {
342 | "x": 126,
343 | "y": 409,
344 | "w": 115,
345 | "h": 131
346 | }
347 | },
348 | {
349 | "filename": "green_idle_0",
350 | "rotated": false,
351 | "trimmed": true,
352 | "sourceSize": {
353 | "w": 265,
354 | "h": 134
355 | },
356 | "spriteSourceSize": {
357 | "x": 148,
358 | "y": 0,
359 | "w": 114,
360 | "h": 132
361 | },
362 | "frame": {
363 | "x": 243,
364 | "y": 538,
365 | "w": 114,
366 | "h": 132
367 | }
368 | },
369 | {
370 | "filename": "blue_die_0",
371 | "rotated": false,
372 | "trimmed": true,
373 | "sourceSize": {
374 | "w": 235,
375 | "h": 138
376 | },
377 | "spriteSourceSize": {
378 | "x": 3,
379 | "y": 1,
380 | "w": 107,
381 | "h": 134
382 | },
383 | "frame": {
384 | "x": 126,
385 | "y": 542,
386 | "w": 107,
387 | "h": 134
388 | }
389 | },
390 | {
391 | "filename": "blue_idle_0",
392 | "rotated": false,
393 | "trimmed": true,
394 | "sourceSize": {
395 | "w": 235,
396 | "h": 138
397 | },
398 | "spriteSourceSize": {
399 | "x": 1,
400 | "y": 2,
401 | "w": 107,
402 | "h": 134
403 | },
404 | "frame": {
405 | "x": 2,
406 | "y": 543,
407 | "w": 107,
408 | "h": 134
409 | }
410 | },
411 | {
412 | "filename": "blue_die_2",
413 | "rotated": false,
414 | "trimmed": true,
415 | "sourceSize": {
416 | "w": 235,
417 | "h": 138
418 | },
419 | "spriteSourceSize": {
420 | "x": 25,
421 | "y": 1,
422 | "w": 106,
423 | "h": 135
424 | },
425 | "frame": {
426 | "x": 359,
427 | "y": 543,
428 | "w": 106,
429 | "h": 135
430 | }
431 | },
432 | {
433 | "filename": "blue_idle_2",
434 | "rotated": false,
435 | "trimmed": true,
436 | "sourceSize": {
437 | "w": 235,
438 | "h": 138
439 | },
440 | "spriteSourceSize": {
441 | "x": 1,
442 | "y": 1,
443 | "w": 106,
444 | "h": 135
445 | },
446 | "frame": {
447 | "x": 235,
448 | "y": 672,
449 | "w": 106,
450 | "h": 135
451 | }
452 | },
453 | {
454 | "filename": "blue_idle_3",
455 | "rotated": false,
456 | "trimmed": true,
457 | "sourceSize": {
458 | "w": 235,
459 | "h": 138
460 | },
461 | "spriteSourceSize": {
462 | "x": 1,
463 | "y": 1,
464 | "w": 106,
465 | "h": 135
466 | },
467 | "frame": {
468 | "x": 235,
469 | "y": 672,
470 | "w": 106,
471 | "h": 135
472 | }
473 | },
474 | {
475 | "filename": "blue_idle_1",
476 | "rotated": false,
477 | "trimmed": true,
478 | "sourceSize": {
479 | "w": 235,
480 | "h": 138
481 | },
482 | "spriteSourceSize": {
483 | "x": 1,
484 | "y": 2,
485 | "w": 106,
486 | "h": 134
487 | },
488 | "frame": {
489 | "x": 111,
490 | "y": 678,
491 | "w": 106,
492 | "h": 134
493 | }
494 | },
495 | {
496 | "filename": "blue_idle_4",
497 | "rotated": false,
498 | "trimmed": true,
499 | "sourceSize": {
500 | "w": 235,
501 | "h": 138
502 | },
503 | "spriteSourceSize": {
504 | "x": 1,
505 | "y": 2,
506 | "w": 106,
507 | "h": 134
508 | },
509 | "frame": {
510 | "x": 111,
511 | "y": 678,
512 | "w": 106,
513 | "h": 134
514 | }
515 | },
516 | {
517 | "filename": "blue_attack_1",
518 | "rotated": false,
519 | "trimmed": true,
520 | "sourceSize": {
521 | "w": 235,
522 | "h": 138
523 | },
524 | "spriteSourceSize": {
525 | "x": 1,
526 | "y": 6,
527 | "w": 106,
528 | "h": 132
529 | },
530 | "frame": {
531 | "x": 2,
532 | "y": 679,
533 | "w": 106,
534 | "h": 132
535 | }
536 | },
537 | {
538 | "filename": "green_die_1",
539 | "rotated": false,
540 | "trimmed": true,
541 | "sourceSize": {
542 | "w": 265,
543 | "h": 134
544 | },
545 | "spriteSourceSize": {
546 | "x": 154,
547 | "y": 1,
548 | "w": 103,
549 | "h": 132
550 | },
551 | "frame": {
552 | "x": 2,
553 | "y": 813,
554 | "w": 103,
555 | "h": 132
556 | }
557 | },
558 | {
559 | "filename": "green-missile",
560 | "rotated": false,
561 | "trimmed": false,
562 | "sourceSize": {
563 | "w": 60,
564 | "h": 11
565 | },
566 | "spriteSourceSize": {
567 | "x": 0,
568 | "y": 0,
569 | "w": 60,
570 | "h": 11
571 | },
572 | "frame": {
573 | "x": 2,
574 | "y": 947,
575 | "w": 60,
576 | "h": 11
577 | }
578 | },
579 | {
580 | "filename": "blue_die_3",
581 | "rotated": false,
582 | "trimmed": true,
583 | "sourceSize": {
584 | "w": 235,
585 | "h": 138
586 | },
587 | "spriteSourceSize": {
588 | "x": 46,
589 | "y": 11,
590 | "w": 113,
591 | "h": 121
592 | },
593 | "frame": {
594 | "x": 343,
595 | "y": 680,
596 | "w": 113,
597 | "h": 121
598 | }
599 | },
600 | {
601 | "filename": "blue_die_1",
602 | "rotated": false,
603 | "trimmed": true,
604 | "sourceSize": {
605 | "w": 235,
606 | "h": 138
607 | },
608 | "spriteSourceSize": {
609 | "x": 1,
610 | "y": 10,
611 | "w": 105,
612 | "h": 126
613 | },
614 | "frame": {
615 | "x": 343,
616 | "y": 803,
617 | "w": 105,
618 | "h": 126
619 | }
620 | },
621 | {
622 | "filename": "green_die_3",
623 | "rotated": false,
624 | "trimmed": true,
625 | "sourceSize": {
626 | "w": 265,
627 | "h": 134
628 | },
629 | "spriteSourceSize": {
630 | "x": 89,
631 | "y": 24,
632 | "w": 119,
633 | "h": 110
634 | },
635 | "frame": {
636 | "x": 219,
637 | "y": 809,
638 | "w": 119,
639 | "h": 110
640 | }
641 | },
642 | {
643 | "filename": "green_die_2",
644 | "rotated": false,
645 | "trimmed": true,
646 | "sourceSize": {
647 | "w": 265,
648 | "h": 134
649 | },
650 | "spriteSourceSize": {
651 | "x": 125,
652 | "y": 0,
653 | "w": 97,
654 | "h": 134
655 | },
656 | "frame": {
657 | "x": 107,
658 | "y": 814,
659 | "w": 97,
660 | "h": 134
661 | }
662 | },
663 | {
664 | "filename": "green_die_4",
665 | "rotated": false,
666 | "trimmed": true,
667 | "sourceSize": {
668 | "w": 265,
669 | "h": 134
670 | },
671 | "spriteSourceSize": {
672 | "x": 65,
673 | "y": 43,
674 | "w": 134,
675 | "h": 91
676 | },
677 | "frame": {
678 | "x": 206,
679 | "y": 921,
680 | "w": 134,
681 | "h": 91
682 | }
683 | },
684 | {
685 | "filename": "blue_die_4",
686 | "rotated": false,
687 | "trimmed": true,
688 | "sourceSize": {
689 | "w": 235,
690 | "h": 138
691 | },
692 | "spriteSourceSize": {
693 | "x": 59,
694 | "y": 63,
695 | "w": 137,
696 | "h": 75
697 | },
698 | "frame": {
699 | "x": 342,
700 | "y": 931,
701 | "w": 137,
702 | "h": 75
703 | }
704 | }
705 | ]
706 | }
707 | ],
708 | "meta": {
709 | "app": "https://www.codeandweb.com/texturepacker",
710 | "version": "3.0",
711 | "smartupdate": "$TexturePacker:SmartUpdate:00aaedca3db7fe8cbcd373df0d41f966:1498babd4a3a2e90f6d2541e19b72d48:da3af008a90a9c9752df55336b1bf14d$"
712 | }
713 | }
714 |
--------------------------------------------------------------------------------
/tests/tests.js:
--------------------------------------------------------------------------------
1 | var expect = chai.expect;
2 | var spy = chai.spy;
3 | var Health = PhaserHealth;
4 |
5 | var game;
6 | var scene;
7 | var sprite;
8 |
9 | mocha.setup({
10 | allowUncaught: true,
11 | bail: true,
12 | ui: 'bdd'
13 | });
14 |
15 | describe('Phaser', function () {
16 | it('is an object', function () {
17 | expect(Phaser).is.an('object');
18 | });
19 |
20 | it('is version 3.24.1', function () {
21 | expect(Phaser).has.property('VERSION', '3.24.1');
22 | });
23 | });
24 |
25 | describe('PhaserHealth', function () {
26 | it('is an object', function () {
27 | expect(PhaserHealth).is.an('object');
28 | });
29 | });
30 |
31 | describe('hooks', function () {
32 | before('create game', function (done) {
33 | game = new Phaser.Game({
34 | type: Phaser.HEADLESS,
35 | scene: {
36 | init: function () {
37 | scene = this;
38 | done();
39 | }
40 | },
41 | callbacks: {
42 | postBoot: function () {
43 | game.loop.stop();
44 | }
45 | }
46 | });
47 | });
48 |
49 | after('destroy game', function () {
50 | game.destroy(true, true);
51 | game.runDestroy();
52 | });
53 |
54 | beforeEach('create sprite and add health component', function () {
55 | sprite = scene.add.sprite();
56 | Health.AddTo(sprite);
57 | });
58 |
59 | afterEach('destroy sprite', function () {
60 | sprite.destroy();
61 | });
62 |
63 | describe('AddTo()', function () {
64 | context('passing a game object', function () {
65 | it('adds the component methods to the object', function () {
66 | expect(sprite)
67 | .respondsTo('damage').and
68 | .respondsTo('getHealth').and
69 | .respondsTo('getHealthFrac').and
70 | .respondsTo('getMaxHealth').and
71 | .respondsTo('getMinHealth').and
72 | .respondsTo('heal').and
73 | .respondsTo('isAlive').and
74 | .respondsTo('isDead').and
75 | .respondsTo('revive').and
76 | .respondsTo('reviveAtMaxHealth').and
77 | .respondsTo('setHealth').and
78 | .respondsTo('setMaxHealth').and
79 | .respondsTo('setMinHealth');
80 | });
81 |
82 | context('passing no health', function () {
83 | it('sets health to 1', function () {
84 | expect(sprite.getHealth()).equals(1);
85 | });
86 |
87 | it('sets minHealth to -Infinity', function () {
88 | expect(sprite.getMinHealth()).equals(-Infinity);
89 | });
90 |
91 | it('sets maxHealth to 100', function () {
92 | expect(sprite.getMaxHealth()).equals(100);
93 | });
94 | });
95 |
96 | context('passing health (only)', function () {
97 | it('sets health', function () {
98 | Health.AddTo(sprite, 2);
99 | expect(sprite.getHealth()).equals(2);
100 | });
101 |
102 | it('sets minHealth to -Infinity', function () {
103 | Health.AddTo(sprite, 2);
104 | expect(sprite.getMinHealth()).equals(-Infinity);
105 | });
106 |
107 | it('sets maxHealth to 100', function () {
108 | Health.AddTo(sprite, 2);
109 | expect(sprite.getMaxHealth()).equals(100);
110 | });
111 | });
112 |
113 | context('passing minHealth', function () {
114 | it('sets minHealth', function () {
115 | Health.AddTo(sprite, 2, 0);
116 | expect(sprite.getMinHealth()).equals(0);
117 | });
118 | });
119 |
120 | context('passing maxHealth', function () {
121 | it('sets maxHealth', function () {
122 | Health.AddTo(sprite, 2, undefined, 3);
123 | expect(sprite.getMaxHealth()).equals(3);
124 | });
125 | });
126 | });
127 |
128 | context('passing an object (with the required methods)', function () {
129 | it('adds the component methods to the object', function () {
130 | var thing = {
131 | data: null,
132 | getData: Phaser.GameObjects.GameObject.prototype.getData,
133 | setData: Phaser.GameObjects.GameObject.prototype.setData,
134 | setDataEnabled: Phaser.GameObjects.GameObject.prototype.setDataEnabled
135 | };
136 |
137 | Object.assign(thing, Object.getPrototypeOf(Phaser.Events.EventEmitter.prototype));
138 |
139 | Phaser.Events.EventEmitter.call(thing);
140 |
141 | thing.data = new Phaser.Data.DataManager(thing);
142 |
143 | Health.AddTo(thing);
144 |
145 | expect(thing)
146 | .respondsTo('damage').and
147 | .respondsTo('getHealth').and
148 | .respondsTo('getHealthFrac').and
149 | .respondsTo('getMaxHealth').and
150 | .respondsTo('getMinHealth').and
151 | .respondsTo('heal').and
152 | .respondsTo('isAlive').and
153 | .respondsTo('isDead').and
154 | .respondsTo('revive').and
155 | .respondsTo('reviveAtMaxHealth').and
156 | .respondsTo('setHealth').and
157 | .respondsTo('setMaxHealth').and
158 | .respondsTo('setMinHealth');
159 | });
160 | });
161 |
162 | context('passing an object (custom implementation)', function () {
163 | it('adds the component methods to the object', function () {
164 | // eslint-disable-next-line no-new-func
165 | var noop = new Function();
166 | var thing = {
167 | data: new Map(),
168 | emit: noop,
169 | off: noop,
170 | on: noop,
171 | once: noop,
172 | getData: function (key) {
173 | return this.data.get(key);
174 | },
175 | setData: function (key, val) {
176 | return this.data.set(key, val);
177 | },
178 | setDataEnabled: noop
179 | };
180 |
181 | Health.AddTo(thing);
182 |
183 | expect(thing)
184 | .respondsTo('damage').and
185 | .respondsTo('getHealth').and
186 | .respondsTo('getHealthFrac').and
187 | .respondsTo('getMaxHealth').and
188 | .respondsTo('getMinHealth').and
189 | .respondsTo('heal').and
190 | .respondsTo('isAlive').and
191 | .respondsTo('isDead').and
192 | .respondsTo('revive').and
193 | .respondsTo('reviveAtMaxHealth').and
194 | .respondsTo('setHealth').and
195 | .respondsTo('setMaxHealth').and
196 | .respondsTo('setMinHealth');
197 |
198 | thing.setHealth(2);
199 | expect(thing.getHealth()).equals(2);
200 | });
201 | });
202 | });
203 |
204 | describe('Dump()', function () {
205 | it('prints to console (without error)', function () {
206 | expect(function () {
207 | Health.Dump([sprite]);
208 | }).not.to.throw();
209 | });
210 | });
211 |
212 | describe('Damage()', function () {
213 | context('passing no amount', function () {
214 | it('decreases the target’s health by 1', function () {
215 | expect(function () {
216 | Health.Damage(sprite);
217 | })
218 | .decreases(function () { return sprite.getHealth(); })
219 | .by(1);
220 | });
221 | });
222 |
223 | context('passing nonzero amount', function () {
224 | it('decreases the target’s health by amount', function () {
225 | expect(function () {
226 | Health.Damage(sprite, 2);
227 | })
228 | .decreases(function () { return sprite.getHealth(); })
229 | .by(2);
230 | });
231 | });
232 | });
233 |
234 | describe('Heal()', function () {
235 | context('passing no amount', function () {
236 | it('increases the target’s health by 1', function () {
237 | expect(function () {
238 | Health.Heal(sprite);
239 | })
240 | .increases(function () {
241 | return sprite.getHealth();
242 | })
243 | .by(1);
244 | });
245 | });
246 |
247 | context('passing a nonzero amount', function () {
248 | it('increases the target’s health by the amount', function () {
249 | expect(function () {
250 | Health.Heal(sprite, 2);
251 | })
252 | .increases(function () {
253 | return sprite.getHealth();
254 | })
255 | .by(2);
256 | });
257 | });
258 | });
259 |
260 | describe('IsAlive()', function () {
261 | context('when health > 0', function () {
262 | it('returns true', function () {
263 | sprite.setHealth(1);
264 | expect(Health.IsAlive(sprite)).is.true;
265 | });
266 | });
267 |
268 | context('when health is 0', function () {
269 | it('returns false', function () {
270 | sprite.setHealth(0);
271 | expect(Health.IsAlive(sprite)).is.false;
272 | });
273 | });
274 |
275 | context('when health < 0', function () {
276 | it('returns false', function () {
277 | sprite.setHealth(-1);
278 | expect(Health.IsAlive(sprite)).is.false;
279 | });
280 | });
281 | });
282 |
283 | describe('IsDead()', function () {
284 | context('when health > 0', function () {
285 | it('returns false', function () {
286 | sprite.setHealth(1);
287 | expect(Health.IsDead(sprite)).is.false;
288 | });
289 | });
290 |
291 | context('when health is 0', function () {
292 | it('returns true', function () {
293 | sprite.setHealth(0);
294 | expect(Health.IsDead(sprite)).is.true;
295 | });
296 | });
297 |
298 | context('when health < 0', function () {
299 | it('returns true', function () {
300 | sprite.setHealth(-1);
301 | expect(Health.IsDead(sprite)).is.true;
302 | });
303 | });
304 | });
305 |
306 | describe('Kill()', function () {
307 | it('changes the target’s health to 0', function () {
308 | Health.Kill(sprite);
309 | expect(sprite.getHealth()).to.equal(0);
310 | });
311 | });
312 |
313 | describe('MixinTo()', function () {
314 | context('passing a class', function () {
315 | it('adds the component methods to the class', function () {
316 | var Thing = function () {
317 | this.events = new Phaser.Events.EventEmitter();
318 | this.data = new Phaser.Data.DataManager(this, this.events);
319 | };
320 |
321 | Object.assign(Thing.prototype, {
322 | getData: Phaser.GameObjects.GameObject.prototype.getData,
323 | setData: Phaser.GameObjects.GameObject.prototype.setData,
324 | setDataEnabled: Phaser.GameObjects.GameObject.prototype.setDataEnabled
325 | });
326 |
327 | Health.MixinTo(Thing);
328 |
329 | expect(Thing)
330 | .respondsTo('damage').and
331 | .respondsTo('getHealth').and
332 | .respondsTo('heal').and
333 | .respondsTo('isAlive').and
334 | .respondsTo('isDead').and
335 | .respondsTo('revive').and
336 | .respondsTo('reviveAtMaxHealth').and
337 | .respondsTo('setHealth').and
338 | .respondsTo('setMaxHealth');
339 | });
340 | });
341 | });
342 |
343 | describe('Revive()', function () {
344 | context('passing no amount', function () {
345 | it('sets the target’s health to 1', function () {
346 | sprite.setHealth(0);
347 | expect(sprite.getHealth()).equals(0);
348 | Health.Revive(sprite);
349 | expect(sprite.getHealth()).equals(1);
350 | });
351 | });
352 |
353 | context('passing an amount', function () {
354 | it('sets the target’s health to the amount', function () {
355 | sprite.setHealth(0);
356 | expect(sprite.getHealth()).equals(0);
357 | Health.Revive(sprite, 2);
358 | expect(sprite.getHealth()).equals(2);
359 | });
360 | });
361 | });
362 |
363 | describe('ReviveAtMaxHealth()', function () {
364 | it('sets the target’s health to its maximum', function () {
365 | sprite.setHealth(0);
366 | expect(sprite.getHealth()).equals(0);
367 | Health.ReviveAtMaxHealth(sprite);
368 | expect(sprite.getHealth()).equals(100);
369 | });
370 | });
371 |
372 | describe('Actions.Damage()', function () {
373 | context('passing no amount', function () {
374 | it('decreases each target’s health by 1', function () {
375 | expect(function () {
376 | Health.Actions.Damage([sprite]);
377 | })
378 | .decreases(function () {
379 | return sprite.getHealth();
380 | })
381 | .by(1);
382 | });
383 | });
384 |
385 | context('passing nonzero amount', function () {
386 | it('decreases each target’s health by amount', function () {
387 | expect(function () {
388 | Health.Actions.Damage([sprite], 2);
389 | })
390 | .decreases(function () {
391 | return sprite.getHealth();
392 | })
393 | .by(2);
394 | });
395 | });
396 | });
397 |
398 | describe('Actions.Heal()', function () {
399 | context('passing no amount', function () {
400 | it('increases each target’s health by 1', function () {
401 | expect(function () {
402 | Health.Actions.Heal([sprite]);
403 | })
404 | .increases(function () {
405 | return sprite.getHealth();
406 | })
407 | .by(1);
408 | });
409 | });
410 |
411 | context('passing a nonzero amount', function () {
412 | it('increases each target’s health by the amount', function () {
413 | expect(function () {
414 | Health.Actions.Heal([sprite], 2);
415 | })
416 | .increases(function () {
417 | return sprite.getHealth();
418 | })
419 | .by(2);
420 | });
421 | });
422 | });
423 |
424 | describe('Actions.Kill()', function () {
425 | it('changes each target’s health to 0', function () {
426 | Health.Actions.Kill([sprite]);
427 | expect(sprite.getHealth()).to.equal(0);
428 | });
429 | });
430 |
431 | describe('Actions.Revive()', function () {
432 | context('passing no amount', function () {
433 | it('sets each target’s health to 1', function () {
434 | sprite.setHealth(0);
435 | expect(sprite.getHealth()).equals(0);
436 | Health.Actions.Revive([sprite]);
437 | expect(sprite.getHealth()).equals(1);
438 | });
439 | });
440 |
441 | context('passing an amount', function () {
442 | it('sets each target’s health to the amount', function () {
443 | sprite.setHealth(0);
444 | expect(sprite.getHealth()).equals(0);
445 | Health.Actions.Revive([sprite], 2);
446 | expect(sprite.getHealth()).equals(2);
447 | });
448 | });
449 | });
450 |
451 | describe('Actions.ReviveAtMaxHealth()', function () {
452 | it('sets the target’s health to its maximum', function () {
453 | sprite.setHealth(0);
454 | expect(sprite.getHealth()).equals(0);
455 | Health.ReviveAtMaxHealth(sprite);
456 | expect(sprite.getHealth()).equals(100);
457 | });
458 | });
459 |
460 | describe('Actions.SetHealth()', function () {
461 | context('passing health', function () {
462 | it('sets the health of each object', function () {
463 | sprite.setHealth(1);
464 | Health.Actions.SetHealth([sprite], 2);
465 | expect(sprite.getHealth()).equals(2);
466 | });
467 | });
468 |
469 | context('passing minHealth', function () {
470 | it('sets the minHealth of each object', function () {
471 | sprite.setMinHealth(-10);
472 | Health.Actions.SetHealth([sprite], 1, 0, 4);
473 | expect(sprite.getMinHealth()).equals(0);
474 | });
475 | });
476 |
477 | context('passing maxHealth', function () {
478 | it('sets the maxHealth of each object', function () {
479 | sprite.setMaxHealth(3);
480 | Health.Actions.SetHealth([sprite], 1, undefined, 4);
481 | expect(sprite.getMaxHealth()).equals(4);
482 | });
483 | });
484 | });
485 |
486 | describe('Events.DAMAGE', function () {
487 | it('is `damage`', function () {
488 | expect(Health.Events.DAMAGE).equals('damage');
489 | });
490 | });
491 |
492 | describe('Events.DIE', function () {
493 | it('is `die`', function () {
494 | expect(Health.Events.DIE).equals('die');
495 | });
496 | });
497 |
498 | describe('Events.HEAL', function () {
499 | it('is `heal`', function () {
500 | expect(Health.Events.HEAL).equals('heal');
501 | });
502 | });
503 |
504 | describe('Events.HEALTH_CHANGE', function () {
505 | it('is `healthchange`', function () {
506 | expect(Health.Events.HEALTH_CHANGE).equals('healthchange');
507 | });
508 | });
509 |
510 | describe('Events.REVIVE', function () {
511 | it('is `revive`', function () {
512 | expect(Health.Events.REVIVE).equals('revive');
513 | });
514 | });
515 |
516 | describe('getHealth()', function () {
517 | it('returns health', function () {
518 | expect(sprite.getHealth()).equals(1);
519 | });
520 | });
521 |
522 | describe('getHealthFrac()', function () {
523 | it('returns (health / maxHealth)', function () {
524 | sprite.setMaxHealth(100);
525 | sprite.setHealth(100);
526 | expect(sprite.getHealthFrac()).equals(1);
527 | sprite.setHealth(10);
528 | expect(sprite.getHealthFrac()).equals(0.1);
529 | sprite.setHealth(0);
530 | expect(sprite.getHealthFrac()).equals(0);
531 | sprite.setHealth(-10);
532 | expect(sprite.getHealthFrac()).equals(-0.1);
533 | });
534 | });
535 |
536 | describe('getMaxHealth()', function () {
537 | it('returns maxHealth', function () {
538 | expect(sprite.getMaxHealth()).equals(100);
539 | });
540 | });
541 |
542 | describe('isAlive()', function () {
543 | context('when health > 0', function () {
544 | it('returns true', function () {
545 | sprite.setHealth(1);
546 | expect(sprite.isAlive()).is.true;
547 | });
548 | });
549 |
550 | context('when health is 0', function () {
551 | it('returns false', function () {
552 | sprite.setHealth(0);
553 | expect(sprite.isAlive()).is.false;
554 | });
555 | });
556 |
557 | context('when health is < 0', function () {
558 | it('returns false', function () {
559 | sprite.setHealth(-1);
560 | expect(sprite.isAlive()).is.false;
561 | });
562 | });
563 | });
564 |
565 | describe('isDead()', function () {
566 | context('when health > 0', function () {
567 | it('returns false', function () {
568 | expect(sprite.isDead()).is.false;
569 | });
570 | });
571 |
572 | context('when health is 0', function () {
573 | it('returns true', function () {
574 | sprite.setHealth(0);
575 | expect(sprite.isDead()).is.true;
576 | });
577 | });
578 |
579 | context('when health is < 0', function () {
580 | it('returns true', function () {
581 | sprite.setHealth(-1);
582 | expect(sprite.isDead()).is.true;
583 | });
584 | });
585 | });
586 |
587 | describe('setHealth()', function () {
588 | context('passing health amount', function () {
589 | it('sets health', function () {
590 | sprite.setHealth(2);
591 | expect(sprite.getHealth()).equals(2);
592 | });
593 |
594 | it('fires `healthchange`, passing the object, change amount, health, minHealth, and maxHealth', function () {
595 | var onHealthChange = spy();
596 | sprite.once('healthchange', onHealthChange).setHealth(2);
597 | expect(onHealthChange).is.called.with.exactly(sprite, 1, 2, -Infinity, 100);
598 | });
599 | });
600 |
601 | context('passing health amount greater than maxHealth', function () {
602 | it('sets health equal to maxHealth, not the greater amount', function () {
603 | sprite.setMaxHealth(100).setHealth(101);
604 | expect(sprite.getHealth()).equals(100);
605 | });
606 |
607 | it('fires `healthchange`, passing the actual change amount', function () {
608 | var onHealthChange = spy();
609 | sprite.once('healthchange', onHealthChange).setMaxHealth(100).setHealth(101);
610 | expect(onHealthChange).is.called.with.exactly(sprite, 99, 100, -Infinity, 100);
611 | });
612 | });
613 |
614 | context('passing health amount less than minHealth', function () {
615 | it('sets health equal to minHealth, not the lesser amount', function () {
616 | sprite.setMinHealth(0).setHealth(-1);
617 | expect(sprite.getHealth()).equals(0);
618 | });
619 |
620 | it('fires `healthchange`, passing the actual change amount', function () {
621 | var onHealthChange = spy();
622 | sprite.once('healthchange', onHealthChange).setMinHealth(0).setHealth(-1);
623 | expect(onHealthChange).is.called.with.exactly(sprite, -1, 0, 0, 100);
624 | });
625 | });
626 |
627 | context('passing health amount equal to current health', function () {
628 | it('does not fire `healthchange`', function () {
629 | var healthchange = spy();
630 | sprite.once('healthchange', healthchange).setHealth(1);
631 | expect(healthchange).is.not.called();
632 | });
633 | });
634 |
635 | context('passing silent = true', function () {
636 | it('does not fire `healthchange`', function () {
637 | var healthchange = spy();
638 | sprite.once('healthchange', healthchange).setHealth(2, undefined, undefined, true);
639 | expect(healthchange).is.not.called();
640 | });
641 | });
642 |
643 | context('health decreases to 0', function () {
644 | it('fires `die`, passing the object', function () {
645 | var onDie = spy();
646 | sprite.setHealth(1).on('die', onDie).setHealth(0);
647 | expect(onDie).is.called.with.exactly(sprite);
648 | });
649 | });
650 |
651 | context('health decreases below 0', function () {
652 | it('fires `die`, passing the object', function () {
653 | var onDie = spy();
654 | sprite.setHealth(1).on('die', onDie).setHealth(-1);
655 | expect(onDie).is.called.with.exactly(sprite);
656 | });
657 | });
658 |
659 | context('health increases above 0', function () {
660 | it('fires `revive`, passing the object', function () {
661 | var onRevive = spy();
662 | sprite.setHealth(-1).on('revive', onRevive).setHealth(1);
663 | expect(onRevive).is.called.with.exactly(sprite);
664 | });
665 | });
666 | });
667 |
668 | describe('setMaxHealth()', function () {
669 | context('passing amount', function () {
670 | it('sets maxHealth', function () {
671 | sprite.setMaxHealth(2);
672 | expect(sprite.getMaxHealth()).equals(2);
673 | });
674 | });
675 |
676 | context('passing amount less than current health', function () {
677 | it('reduces health to maxHealth', function () {
678 | sprite.setHealth(3).setMaxHealth(2);
679 | expect(sprite.getHealth()).equals(2);
680 | });
681 |
682 | it('fires `healthchange`, passing the object, change amount, health, minHealth, and maxHealth', function () {
683 | var healthchange = spy();
684 | sprite.setHealth(3).once('healthchange', healthchange).setMaxHealth(2);
685 | expect(healthchange).is.called.with.exactly(sprite, -1, 2, -Infinity, 2);
686 | });
687 | });
688 | });
689 |
690 | describe('damage()', function () {
691 | context('passing no amount', function () {
692 | it('decreases health by 1', function () {
693 | expect(function () { sprite.damage(); })
694 | .decreases(function () { return sprite.getHealth(); })
695 | .by(1);
696 | });
697 |
698 | it('fires `damage`, passing the object and absolute damage amount', function () {
699 | var onDamage = spy();
700 | sprite.once('damage', onDamage).damage();
701 | expect(onDamage).is.called.with.exactly(sprite, 1);
702 | });
703 |
704 | it('fires `healthchange`, passing the object, change amount, health, minHealth, and maxHealth', function () {
705 | var onHealthChange = spy();
706 | sprite.once('healthchange', onHealthChange).damage();
707 | expect(onHealthChange).is.called.with.exactly(sprite, -1, 0, -Infinity, 100);
708 | });
709 | });
710 |
711 | context('passing nonzero amount', function () {
712 | it('decreases health by amount', function () {
713 | expect(function () { sprite.damage(2); })
714 | .decreases(function () { return sprite.getHealth(); })
715 | .by(2);
716 | });
717 |
718 | it('fires `damage`, passing the object and absolute damage amount', function () {
719 | var onDamage = spy();
720 | sprite.once('damage', onDamage).damage(2);
721 | expect(onDamage).is.called.with.exactly(sprite, 2);
722 | });
723 |
724 | it('fires `healthchange`, passing the object, change amount, health, minHealth, and maxHealth', function () {
725 | var onHealthChange = spy();
726 | sprite.once('healthchange', onHealthChange).damage(2);
727 | expect(onHealthChange).is.called.with.exactly(sprite, -2, -1, -Infinity, 100);
728 | });
729 | });
730 |
731 | context('passing amount = 0', function () {
732 | it('does not fire `damage`', function () {
733 | var onDamage = spy();
734 | sprite.once('damage', onDamage).damage(0);
735 | expect(onDamage).is.not.called();
736 | });
737 |
738 | it('does not fire `healthchange`', function () {
739 | var onHealthChange = spy();
740 | sprite.once('healthchange', onHealthChange).damage(0);
741 | expect(onHealthChange).is.not.called();
742 | });
743 | });
744 |
745 | context('passing amount greater than (minHealth - health)', function () {
746 | it('fires `damage` with the actual damage amount, not the greater amount', function () {
747 | var onDamage = spy();
748 | sprite.on('damage', onDamage).setMinHealth(0).damage(100);
749 | expect(onDamage).is.called.with.exactly(sprite, 1);
750 | });
751 |
752 | it('fires `healthchange` with the actual change amount', function () {
753 | var onHealthChange = spy();
754 | sprite.on('healthchange', onHealthChange).setMinHealth(0).damage(100);
755 | expect(onHealthChange).is.called.with.exactly(sprite, -1, 0, 0, 100);
756 | });
757 | });
758 |
759 | context('passing silent = true', function () {
760 | it('does not fire `damage`', function () {
761 | var onDamage = spy();
762 | sprite.once('damage', onDamage).damage(1, true);
763 | expect(onDamage).is.not.called();
764 | });
765 |
766 | it('does not fire `healthchange`', function () {
767 | var onHealthChange = spy();
768 | sprite.once('healthchange', onHealthChange).damage(1, true);
769 | expect(onHealthChange).is.not.called();
770 | });
771 | });
772 | });
773 |
774 | describe('heal()', function () {
775 | context('passing no amount', function () {
776 | it('increases health by 1', function () {
777 | expect(function () { sprite.heal(); })
778 | .increases(function () { return sprite.getHealth(); })
779 | .by(1);
780 | });
781 |
782 | it('fires `heal`, passing the object and heal amount', function () {
783 | var onHeal = spy();
784 | sprite.once('heal', onHeal).heal();
785 | expect(onHeal).is.called.with.exactly(sprite, 1);
786 | });
787 |
788 | it('fires `heal` after health changes', function () {
789 | expect(sprite.getHealth()).equals(1);
790 | sprite.once('heal', function () {
791 | expect(sprite.getHealth()).equals(2);
792 | }).heal();
793 | });
794 | });
795 |
796 | context('passing nonzero amount', function () {
797 | it('increases health by amount', function () {
798 | expect(function () { sprite.heal(2); })
799 | .increases(function () { return sprite.getHealth(); })
800 | .by(2);
801 | });
802 |
803 | it('fires `heal`, passing the object and heal amount', function () {
804 | var onHeal = spy();
805 | sprite.once('heal', onHeal).heal(2);
806 | expect(onHeal).is.called.with.exactly(sprite, 2);
807 | });
808 | });
809 |
810 | context('passing amount = 0', function () {
811 | it('does not fire `heal`', function () {
812 | var onHeal = spy();
813 | sprite.once('heal', onHeal).heal(0);
814 | expect(onHeal).is.not.called();
815 | });
816 |
817 | it('does not fire `healthchange`', function () {
818 | var onHealthChange = spy();
819 | sprite.once('healthchange', onHealthChange).heal(0);
820 | expect(onHealthChange).is.not.called();
821 | });
822 | });
823 |
824 | context('passing amount greater than (maxHealth - health)', function () {
825 | it('fires `heal` with the actual heal amount', function () {
826 | var onHeal = spy();
827 | sprite.on('heal', onHeal).heal(100);
828 | expect(onHeal).is.called.with.exactly(sprite, 99);
829 | });
830 |
831 | it('fires `healthchange` with the actual change amount', function () {
832 | var onHealthChange = spy();
833 | sprite.on('healthchange', onHealthChange).heal(100);
834 | expect(onHealthChange).is.called.with.exactly(sprite, 99, 100, -Infinity, 100);
835 | });
836 | });
837 |
838 | context('passing silent = true', function () {
839 | it('does not fire `heal`', function () {
840 | var onHeal = spy();
841 | sprite.once('heal', onHeal).heal(1, true);
842 | expect(onHeal).is.not.called();
843 | });
844 |
845 | it('does not fire `healthchange`', function () {
846 | var onHealthChange = spy();
847 | sprite.once('healthchange', onHealthChange).heal(1, true);
848 | expect(onHealthChange).is.not.called();
849 | });
850 | });
851 | });
852 |
853 | describe('kill()', function () {
854 | context('when object is alive', function () {
855 | it('sets health to 0', function () {
856 | sprite.setHealth(1);
857 | sprite.kill();
858 | expect(sprite.getHealth()).equals(0);
859 | });
860 | });
861 | context('when object is dead', function () {
862 | it('does not change health', function () {
863 | sprite.setHealth(-1);
864 | sprite.kill();
865 | expect(sprite.getHealth()).equals(-1);
866 | });
867 | });
868 | });
869 |
870 | describe('revive()', function () {
871 | context('passing no amount', function () {
872 | context('when object is alive', function () {
873 | it('does not change health', function () {
874 | sprite.setHealth(2);
875 | sprite.revive();
876 | expect(sprite.getHealth()).equals(2);
877 | });
878 | });
879 | context('when object is dead', function () {
880 | it('sets health to 1', function () {
881 | sprite.kill();
882 | sprite.revive();
883 | expect(sprite.getHealth()).equals(1);
884 | });
885 | });
886 | });
887 |
888 | context('passing an amount', function () {
889 | context('when object is alive', function () {
890 | it('does not change health', function () {
891 | sprite.setHealth(1);
892 | sprite.revive(2);
893 | expect(sprite.getHealth()).equals(1);
894 | });
895 | });
896 | context('when object is dead', function () {
897 | it('sets health to the amount', function () {
898 | sprite.kill();
899 | sprite.revive(2);
900 | expect(sprite.getHealth()).equals(2);
901 | });
902 | });
903 | });
904 | });
905 |
906 | describe('reviveAtMaxHealth()', function () {
907 | context('when object is alive', function () {
908 | it('does not change health', function () {
909 | sprite.setHealth(1);
910 | sprite.reviveAtMaxHealth();
911 | expect(sprite.getHealth()).equals(1);
912 | });
913 | });
914 | context('when object is dead', function () {
915 | it('sets health to maximum', function () {
916 | sprite.kill();
917 | sprite.reviveAtMaxHealth();
918 | expect(sprite.getHealth()).equals(100);
919 | });
920 | });
921 | });
922 | });
923 |
924 | mocha.checkLeaks();
925 | mocha.globals(['Phaser']);
926 | mocha.run();
927 |
--------------------------------------------------------------------------------