/* global platypus */
import Vector from '../Vector.js';
import createComponentClass from '../factory.js';
export default (function () {
var processDirection = function (direction) {
return function (state) {
this[direction] = !state || (state.pressed !== false);
};
},
doNothing = function () {},
rotate = {
x: function (heading, lastHeading) {
if (heading !== lastHeading) {
if (((heading > 180) && (lastHeading <= 180)) || ((heading <= 180) && (lastHeading > 180))) {
this.owner.triggerEvent('transform', 'vertical');
}
}
},
y: function (heading, lastHeading) {
if (heading !== lastHeading) {
if (((heading > 90 && heading <= 270) && (lastHeading <= 90 || lastHeading > 270)) || ((heading <= 90 || heading > 270) && (lastHeading > 90 && lastHeading <= 270))) {
this.owner.triggerEvent('transform', 'horizontal');
}
}
},
z: function (heading, lastHeading) {
if (heading !== lastHeading) {
this.owner.triggerEvent('replace-transform', 'rotate-' + heading);
}
}
};
return createComponentClass(/** @lends platypus.components.LogicDirectionalMovement.prototype */{
id: 'LogicDirectionalMovement',
properties: {
/**
* Defines the axis around which the entity should be transformed. Defaults to "y" for platforming behavior. Use "z" for top-down behavior.
*
* @property axis
* @type String
* @default "y"
*/
axis: 'y',
/**
* Defines the distance in world units that the entity should be moved per millisecond.
*
* @property speed
* @type Number
* @default 0.3
*/
speed: 0.3
},
/**
* This component changes the [Motion](platypus.components.Motion.html) of an entity according to its current speed and heading. It accepts directional messages that can stand alone, or come from a mapped controller, in which case it checks the `pressed` value of the message before changing its course.
*
* @memberof platypus.components
* @uses platypus.Component
* @constructs
* @listens platypus.Entity#accelerate
* @listens platypus.Entity#component-added
* @listens platypus.Entity#face
* @listens platypus.Entity#handle-logic
* @listens platypus.Entity#stop
* @listens platypus.Entity#go-down
* @listens platypus.Entity#go-south
* @listens platypus.Entity#go-down-left
* @listens platypus.Entity#go-southwest
* @listens platypus.Entity#go-left
* @listens platypus.Entity#go-west
* @listens platypus.Entity#go-up-left
* @listens platypus.Entity#go-northwest
* @listens platypus.Entity#go-up
* @listens platypus.Entity#go-north
* @listens platypus.Entity#go-up-right
* @listens platypus.Entity#go-northeast
* @listens platypus.Entity#go-right
* @listens platypus.Entity#go-east
* @listens platypus.Entity#go-down-right
* @listens platypus.Entity#go-southeast
* @fires platypus.Entity#replace-transform
* @fires platypus.Entity#transform
*/
initialize: function () {
var state = this.state = this.owner.state;
if (typeof this.speed === 'number') {
this.speed = [this.speed, 0, 0];
}
this.initialVector = Vector.setUp(this.speed);
this.reorient = rotate[this.axis];
if (!this.reorient) {
this.reorient = doNothing;
}
this.moving = state.set('moving', false);
this.left = state.set('left', false);
this.right = state.set('right', false);
this.up = state.set('up', false);
this.down = state.set('down', false);
this.upLeft = false;
this.upRight = false;
this.downLeft = false;
this.downRight = false;
this.heading = 0;
this.owner.heading = this.owner.heading || 0;
},
events: {
"component-added": function (component) {
if (component === this) {
if (!this.owner.addMover) {
platypus.debug.warn('The "LogicDirectionalMovement" component requires a "Mover" component to function correctly.');
return;
}
this.direction = this.owner.addMover({
velocity: this.speed,
drag: 0,
friction: 0,
stopOnCollision: false,
orient: false,
aliases: {
"moving": "control-velocity"
}
}).velocity;
if (this.owner.heading !== this.heading) {
this.direction.setVector(this.initialVector).rotate((this.owner.heading / 180) * Math.PI);
this.heading = this.owner.heading;
}
this.owner.triggerEvent('moving', this.moving);
}
},
"handle-logic": function () {
var state = this.state,
up = this.up || this.upLeft || this.upRight,
upLeft = this.upLeft || (this.up && this.left),
left = this.left || this.upLeft || this.downLeft,
downLeft = this.downLeft || (this.down && this.left),
down = this.down || this.downLeft || this.downRight,
downRight = this.downRight || (this.down && this.right),
right = this.right || this.upRight || this.downRight,
upRight = this.upRight || (this.up && this.right);
if ((left && right) || (up && down)) {
this.moving = false;
} else if (upLeft) {
this.moving = true;
this.heading = 225;
} else if (upRight) {
this.moving = true;
this.heading = 315;
} else if (downLeft) {
this.moving = true;
this.heading = 135;
} else if (downRight) {
this.moving = true;
this.heading = 45;
} else if (left) {
this.moving = true;
this.heading = 180;
} else if (right) {
this.moving = true;
this.heading = 0;
} else if (up) {
this.moving = true;
this.heading = 270;
} else if (down) {
this.moving = true;
this.heading = 90;
} else {
this.moving = false;
// This is to retain the entity's direction even if there is no movement. There's probably a better way to do this since this is a bit of a retrofit. - DDD
switch (this.heading) {
case 270:
up = true;
break;
case 90:
down = true;
break;
case 180:
left = true;
break;
case 225:
up = true;
left = true;
break;
case 315:
up = true;
right = true;
break;
case 135:
down = true;
left = true;
break;
case 45:
down = true;
right = true;
break;
case 0:
default:
right = true;
break;
}
}
if (this.owner.heading !== this.heading) {
this.direction.setVector(this.initialVector).rotate((this.heading / 180) * Math.PI);
this.reorient(this.heading, this.owner.heading);
this.owner.heading = this.heading;
}
//TODO: possibly remove the separation of this.state.direction and this.direction to just use state?
if (state.get('moving') !== this.moving) {
this.owner.triggerEvent('moving', this.moving);
state.set('moving', this.moving);
}
state.set('up', up);
state.set('right', right);
state.set('down', down);
state.set('left', left);
},
"go-down": processDirection('down'),
"go-south": processDirection('down'),
"go-down-left": processDirection('downLeft'),
"go-southwest": processDirection('downLeft'),
"go-left": processDirection('left'),
"go-west": processDirection('left'),
"go-up-left": processDirection('upLeft'),
"go-northwest": processDirection('upLeft'),
"go-up": processDirection('up'),
"go-north": processDirection('up'),
"go-up-right": processDirection('upRight'),
"go-northeast": processDirection('upRight'),
"go-right": processDirection('right'),
"go-east": processDirection('right'),
"go-down-right": processDirection('downRight'),
"go-southeast": processDirection('downRight'),
"stop": function (state) {
if (!state || (state.pressed !== false)) {
this.left = false;
this.right = false;
this.up = false;
this.down = false;
this.upLeft = false;
this.upRight = false;
this.downLeft = false;
this.downRight = false;
}
},
/**
* Set the direction the entity should face while stopped.
*
* @event platypus.Entity#face
* @param direction {String} A value such as "north" or "left" to point the entity in a particular direction.
*/
"face": (function () {
var headings = {
up: 270,
north: 270,
down: 90,
south: 90,
left: 180,
west: 180,
right: 0,
east: 0,
"up-left": 225,
northwest: 225,
"up-right": 315,
northeast: 315,
"down-left": 135,
southwest: 135,
"down-right": 45,
southeast: 45
};
return function (direction) {
this.heading = headings[direction] || 0;
};
}()),
/**
* Changes the velocity of the Entity when in motion.
*
* @event platypus.Entity#accelerate
* @param velocity {Number|platypus.Vector} The magnitude or Vector to multiply the current velocity by.
*/
"accelerate": function (velocity) {
this.initialVector.normalize().multiply(velocity);
this.direction.normalize().multiply(velocity);
}
},
methods: {
destroy: function () {
this.initialVector.recycle();
this.state = null;
}
}
});
}());