├── 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 | --------------------------------------------------------------------------------