diff --git a/src/direction.js b/src/direction.js index d65aac5..f82a5b9 100644 --- a/src/direction.js +++ b/src/direction.js @@ -16,4 +16,9 @@ export default class Direction { } } + static getModifier (pos, target) { + const _mod = (axis) => (target[axis] - pos[axis]) / Math.abs(target[axis] - pos[axis]); + return {x: _mod('x') || 0, y: _mod('y') || 0}; + } + } diff --git a/src/player.js b/src/player.js index f0c064f..53bc487 100644 --- a/src/player.js +++ b/src/player.js @@ -18,7 +18,6 @@ export default class Player extends Animated(EventEmitting()) { super(properties); this.properties = properties; this.tile = {x, y}; - this.previousTile = this.tile; this.pos = { x: this.tile.x * this.properties.tileWidth + this.texture.width / 2, y: this.tile.y * this.properties.tileHeight + this.texture.height / 2 @@ -109,11 +108,6 @@ export default class Player extends Animated(EventEmitting()) { this.context.drawImage(this.getFrame(this.properties.files, this.direction), getFrameX(offset), getFrameY(offset)); } - get previousTile () {return this._previousTile} - set previousTile (tile) { - this._previousTile = tile; - } - get hadPath () {return this._hadPath || false} set hadPath (value) { this._hadPath = value; @@ -139,37 +133,52 @@ export default class Player extends Animated(EventEmitting()) { }; } - move () { + updateTile ({x, y}) { + const nextTile = this.properties.layer.getXYCoords(x, y); + if (this.tile.x !== nextTile.x || this.tile.y !== nextTile.y) { + this.createEvent("changeTile", nextTile); + this.tile = nextTile; + } + } + + _moveInPath () { + if (this.inPosition.x && this.inPosition.y) { + this.path.shift(); + this.createEvent("pathComplete", this.path); + } else { + this._moveTowardsTarget(); + } + } + + _moveTowardsTarget () { const speed = this.properties.speed; + const modifier = Direction.getModifier(this.pos, this.target); + this.pos = { + x: this.pos.x + modifier.x * speed, + y: this.pos.y + modifier.y * speed + }; + const nextTile = { + x: this.tile.x + modifier.x, + y: this.tile.y + modifier.y + }; + this.direction = Direction.from(this.tile, nextTile); + } + + _completeMovement () { + if (this.hadPath) { + this.createEvent("movementComplete"); + this.hadPath = false; + } + } + + move () { if (this.path.length > 0) { this.hadPath = true; this.stepAnimation(); - - if (this.inPosition.x && this.inPosition.y) { - this.path.shift(); - this.createEvent("pathComplete", this.path); - } else { - if (!this.inPosition.x) { - const modifier = (this.target.x - this.pos.x) / Math.abs(this.target.x - this.pos.x); - this.direction = modifier > 0 ? Direction.RIGHT : Direction.LEFT; - this.pos.x += modifier * speed; - } - if (!this.inPosition.y) { - const modifier = (this.target.y - this.pos.y) / Math.abs(this.target.y - this.pos.y); - this.direction = modifier > 0 ? Direction.DOWN : Direction.UP; - this.pos.y += modifier * speed; - } - } + this._moveInPath(); } else { - if (this.hadPath) { - this.createEvent("movementComplete"); - this.hadPath = false; - } - } - this.tile = this.properties.layer.getXYCoords(this.pos.x, this.pos.y); - if (this.tile.x !== this.previousTile.x || this.tile.y !== this.previousTile.y) { - this.createEvent("changeTile", this.tile); - this.previousTile = this.tile; + this._completeMovement(); } + this.updateTile(this.pos); } } diff --git a/test/direction.js b/test/direction.js index 50728e1..33d435e 100644 --- a/test/direction.js +++ b/test/direction.js @@ -41,3 +41,18 @@ test('#tileInDirection works for specified step', t => { t.deepEqual(Direction.tileInDirection(P, Direction.DOWN, 8), {x: P.x, y: P.y + 8}); t.deepEqual(Direction.tileInDirection(P, Direction.UP, 6), {x: P.x, y: P.y - 6}); }); + +test('#getModifier determines on which axis a target is situated', t => { + const P = {x: 5, y: 7}; + t.deepEqual(Direction.getModifier(P, P), {x: 0, y: 0}); + + t.deepEqual(Direction.getModifier(P, {x: 5, y: 8}), {x: 0, y: 1}); + t.deepEqual(Direction.getModifier(P, {x: 9, y: 7}), {x: 1, y: 0}); + t.deepEqual(Direction.getModifier(P, {x: 5, y: 4}), {x: 0, y: -1}); + t.deepEqual(Direction.getModifier(P, {x: 1, y: 7}), {x: -1, y: 0}); + + t.deepEqual(Direction.getModifier(P, {x: 6, y: 8}), {x: 1, y: 1}); + t.deepEqual(Direction.getModifier(P, {x: 2, y: 9}), {x: -1, y: 1}); + t.deepEqual(Direction.getModifier(P, {x: 6, y: 4}), {x: 1, y: -1}); + t.deepEqual(Direction.getModifier(P, {x: 3, y: 4}), {x: -1, y: -1}); +}); diff --git a/test/player.js b/test/player.js index aaf5805..acbcf8c 100644 --- a/test/player.js +++ b/test/player.js @@ -136,3 +136,97 @@ test('#inPosition works for set targets', t => { t.truthy(player.inPosition.x); t.truthy(player.inPosition.y); }); + +test('#_completeMovement emits an event if a path was completed', t => { + const player = new Player({}, {}, 3, 6); + t.plan(2); + player.on('movementComplete', () => t.pass()); + player._completeMovement(); // should not send event + player.hadPath = true; + player._completeMovement(); // should send event + t.falsy(player.hadPath); +}); + +test('#_moveTowardsTarget moves the player by [speed] pixels into the direction of the target', t => { + const SPEED = 3; + const player = new Player({}, {speed: SPEED}, 3, 6); + const startPos = player.pos; + + player.target = {x: 3, y: 7}; + player._moveTowardsTarget(); + + t.is(player.pos.y, startPos.y + SPEED); +}); + +test('#_moveTowardsTarget lets the player look into the direction of the target', t => { + const player = new Player({}, {}, 5, 3); + player.direction = Direction.UP; + + player.target = {x: 3, y: 3}; + player._moveTowardsTarget(); + + t.is(player.direction, Direction.LEFT); +}); + +test('#_moveInPath moves towards the first path element', t => { + const player = new Player({}, {}, 5, 3); + const startPos = player.pos; + player.path = [{x: 5, y: 2}, {x: 4, y: 2}]; + player._moveInPath(); + t.is(player.pos.y, startPos.y - 1); +}); + +test('#_moveInPath emits an event if it´s path is finished', t => { + const player = new Player({}, {}, 5, 3); + const startPos = player.pos; + t.plan(2); + player.on('pathComplete', () => t.pass()); + player.path = []; + player._moveInPath(); + t.deepEqual(player.pos, startPos); +}); + +test('#_moveInPath emits an event if it´s target is reached', t => { + t.plan(3); + const player = new Player({}, {}, 6, 7); + const startPos = player.pos; + const [FIRST, SECOND] = [player.tile, {x: 2, y: 6}]; + player.on('pathComplete', (p, newPath) => { + t.is(p, player); + t.deepEqual(newPath, [SECOND]); + }); + player.path = [FIRST, SECOND]; + player._moveInPath(); + t.deepEqual(player.pos, startPos); +}); + +test('#updateTile changes the tile according to the new position', t => { + const player = new Player({}, { + layer: { + getXYCoords: (x, y) => { + return {x: Math.floor(x / 32), y: Math.floor(y / 32)} + } + } + }, 6, 7); + const startTile = player.tile; + player.updateTile({x: player.pos.x + 32, y: player.pos.y}); + t.notDeepEqual(player.tile, startTile); + t.deepEqual(player.tile, {x: 7, y: 7}); +}); + +test('#updateTile emits an event when actually changing the tile', t => { + const TILE = {x: 1, y: 2}; + const player = new Player({}, { + layer: { + getXYCoords: (x, y) => TILE + } + }, 6, 7); + const startTile = player.tile; + player.on('changeTile', (p, newTile) => { + t.is(p, player); + t.deepEqual(newTile, TILE); + }); + t.plan(2); + player.updateTile({x: 1, y: 1}); + player.updateTile({x: 1, y: 1}); +});