'use strict';
/**
* @module entity/Entity
*/
const _ = require('lodash');
const uuid = require('../util/uuid');
let h;
let w;
let offx;
let offy;
/**
* A lookup table for directions
*/
const DIRECTIONS = ['up', 'right', 'down', 'left'];
/**
* The `Entity` class is the base class for all game entities.
* It needs to have to following properties
*
* - Health (Upto the child class to define)
* - Weapon
* - Inventory @todo
* - Factions @todo
*
* It also cannot step outside world bounds.
*
* @param {number} x - The x coordinate of `Entity` on the canvas
* @param {number} y - The y coordinate of `Entity` on the canvas
* @param {string} key - The key to the loaded spritesheet
* @constructor Entity
* @see Phaser.Sprite
*/
function Entity(x, y, key) {
Phaser.Sprite.call(this, game, x, y, key);
game.add.existing(this);
game.physics.enable(this, Phaser.Physics.ARCADE);
this.body.collideWorldBounds = true;
/**
* Direction initialized to down.
* Must be changed only when new direction is chosen.
*/
this.direction = 'down';
/**
* Begin with no weapon.
*
* @todo: The weapons can be stored as an object with attributes.
*/
this.weapon = null;
/**
* Miscellaneous attributes.
*/
this.speed = 65;
this.sprintSpeed = 170;
// Set the default animations
this.setAnimations();
game.physics.enable(this, Phaser.Physics.ARCADE);
/**
* hitbox fix
*/
this.body.height = this.body.height / 2;
this.body.width = this.body.width / 2;
this.body.offset.x += this.body.width / 2;
this.body.offset.y += this.body.height;
// /**
// * New Hitbox fix
// */
// this.collideBox = game.add.sprite(0, 0, 'as');
// game.physics.enable(this.collideBox, Phaser.Physics.ARCADE);
// this.collideBox.anchor.setTo(-0.5);
// // this.collideBox.visible = false;
// // this.collideBox.frame = 12;
// // this.collideBox.body.setSize(this.body.height/2, this.body.width/2);
// this.collideBox.body.height = this.body.height/2;
// this.collideBox.body.width = this.body.width/2;
// this.addChild(this.collideBox);
// Set size constants
h = this.body.height;
w = this.body.width;
offx = this.body.offset.x;
offy = this.body.offset.y;
/**
* States.
* State can be 'idling', 'walking', 'attacking'
*/
this.state = 'idling';
this.idleTimer = 0;
this.directionLimiter = 0;
/**
* Type and ID
*/
this.type = 'generic';
this.id = uuid();
/**
* Reputation and Gossip
*/
this.reputation = 0;
this.information = [];
this.dislike = [];
}
Entity.prototype = Object.create(Phaser.Sprite.prototype);
Entity.prototype.constructor = Entity;
/**
* Set the animations of the `Entity`.
*
*
* @param {object} frames - Object containing the animation frames
*/
Entity.prototype.setAnimations = function(frames) {
if (frames !== undefined) {
// Do something with frames
}; // Else set to default animations
/**
* Adding animations for the `Entity`.
*
* 1 sprite sheet contains every movement.
* You target sections of the sprite sheet by using array[0...n],
* where 0 is the top left corner of the image and n is the bottom
* right corner of the image. Spritesheets and their corresponding integers
* count left to right, top to bottom.
*/
this.animations.add('idle_up', [104], 10, true);
this.animations.add('idle_right', [143], 10, true);
this.animations.add('idle_down', [130], 10, true);
this.animations.add('idle_left', [117], 10, true);
this.animations.add('die', [260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 271, 271,
271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271,
271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271,
271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271,
271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271,
271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271,
271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 272],
25,
false);
this.animations.add('walk_up',
[105, 106, 107, 108, 109, 110, 111, 112],
10, true);
this.animations.add('walk_down',
[131, 132, 133, 134, 135, 136, 137, 138],
10, true);
this.animations.add('walk_left',
[118, 119, 120, 121, 122, 123, 124, 125],
10, true);
this.animations.add('walk_right',
[144, 145, 146, 147, 148, 149, 150, 151],
10, true);
this.animations.add('slash_up',
[156, 157, 158, 159, 160, 161],
10, true);
this.animations.add('slash_down',
[182, 183, 184, 185, 186, 187],
10, true);
this.animations.add('slash_left',
[169, 170, 171, 172, 173, 174],
10, true);
this.animations.add('slash_right',
[195, 196, 197, 198, 199, 200],
10, true);
};
/**
* Method to move any `Entity`
*
* The parameter `direction` has to be one of 'up', 'down', 'left' or 'right'.
*
* Another option is to use:
*
* 1. UP
* 2. RIGHT
* 3. DOWN
* 4. LEFT
*
* @param {string|number} direction
* @param {Boolean} sprint - Whether to sprint or not
*/
Entity.prototype.moveInDirection = function(direction, sprint) {
if (this.state !== 'attacking') {
this.state = 'walking';
let speed = this.speed;
let animSpeed = 10;
if (sprint) {
speed = this.sprintSpeed;
animSpeed = 30;
}
this.animations.currentAnim.speed = animSpeed;
let dir = '';
if (_.isString(direction) && _.includes(DIRECTIONS, direction)) {
dir = direction.toLowerCase();
} else if (_.isNumber(direction) && _.inRange(direction, 0, 4)) {
dir = DIRECTIONS[direction];
} else {
console.error(direction);
console.error('Invalid direction');
return;
}
switch (dir) {
case 'up':
this.body.velocity.y = -speed;
this.body.velocity.x = 0;
this.direction = 'up';
break;
case 'down':
this.body.velocity.y = speed;
this.body.velocity.x = 0;
this.direction = 'down';
break;
case 'right':
this.body.velocity.x = speed;
this.body.velocity.y = 0;
this.direction = 'right';
break;
case 'left':
this.body.velocity.x = -speed;
this.body.velocity.y = 0;
this.direction = 'left';
break;
default:
console.error('Invalid direction');
return;
}
this.animations.play('walk_' + dir, animSpeed, true);
this.adjustHitbox('walk');
}
};
Entity.prototype.idleHere = function() {
this.state = 'idling';
this.body.velocity.x = 0;
this.body.velocity.y = 0;
this.animations.play('idle_' + this.direction, 1, false);
this.adjustHitbox('idle');
};
Entity.prototype.attack = function() {
self = this;
// console.log('attacking');
this.state = 'attacking';
this.body.velocity.x = 0;
this.body.velocity.y = 0;
this.animations.play('slash_' + this.direction, 20, false).onComplete.add(function() {
// this.animations.frame
// console.log('attack finished');
self.idleHere();
});
this.adjustHitbox('slash');
};
Entity.prototype.die = function() {
// const self = this;
this.state = 'dead';
this.body.velocity.x = 0;
this.body.velocity.y = 0;
this.alive = false;
// this.exists = false;
this.animations.play('die', 10, false);
const self = this;
setTimeout(function() {
self.kill();
}, 5000);
};
/*
* This function changes the size of the Entity's hit box based on what
* action they are performing and what direction they are facing.
*/
Entity.prototype.adjustHitbox = function(state) {
switch (state) {
case ('walk'):
this.body.height = h;
this.body.width = w;
this.body.offset.y = offy;
this.body.offset.x = offx;
break;
case ('idle'):
this.body.height = h;
this.body.width = w;
this.body.offset.y = offy;
this.body.offset.x = offx;
break;
case ('slash'):
switch (this.direction) {
case ('up'):
this.body.height = 1.5 * h;
this.body.offset.y = h / 2;
break;
case ('down'):
this.body.height = 1.5 * h;
break;
case ('right'):
this.body.width = 1.5 * w;
break;
case ('left'):
this.body.width = 1.5 * w;
this.body.offset.x = offx - (w / 2);
break;
}
break;
}
};
/**
* Set the direction of the sprite
*
* @param {string|number} direction
*/
Entity.prototype.setDirection= function(direction) {
if (_.isString(direction) && _.includes(DIRECTIONS, direction)) {
this.direction = direction.toLowerCase();
} else if (_.isNumber(direction) && _.inRange(direction, 1, 5)) {
this.direction = DIRECTIONS[direction];
} else {
console.error('Invalid direction');
}
};
Entity.prototype.serialize = function() {
let obj = {};
obj.id = this.id;
obj.x = this.x;
obj.y = this.y;
obj.key = this.key;
obj.alive = this.alive;
obj.type = this.type;
obj.information = this.information;
obj.reputation = this.reputation;
if (this.type === 'player') {
obj.score = this.score;
obj.daysSurvived = this.daysSurvived;
}
return obj;
};
Entity.prototype.deserialize = function(obj) {
this.id = obj.id;
this.x = obj.x;
this.y = obj.y;
this.key = obj.key;
this.alive = obj.alive;
this.type = obj.type;
this.information = obj.information;
this.reputation = obj.reputation;
if (this.type === 'player') {
this.score = obj.score;
this.daysSurvived = obj.daysSurvived;
}
};
/**
* Return the Name of the function.
* This is a hack and should be used only for debugging.
*
* @return {string}
*/
Entity.prototype.toString = function() {
let funcNameRegex = /function (.{1,})\(/;
let results = (funcNameRegex).exec((this).constructor.toString());
return (results && results.length > 1) ? results[1] : '';
};
/**
* Get the center of the Hitbox of the entity
*
* @return {Object} - Point with x and y
*/
Entity.prototype.trueXY = function() {
const self = this;
return {
x: self.x + self.body.width/2 + self.body.offset.x,
y: self.y + self.body.height/2 + self.body.offset.y,
};
};
/**
*
*
* @param {Object} rumor
*/
Entity.prototype.learnInfo = function(rumor) {
if (!this.alive) return;
if (this.information.some((e) => e.id === rumor.id)) {
/**
* Do nothing if we already know this information.
*/
return;
}
console.info('[' + this.id +'] Learning something new....')
this.information.push(rumor);
switch (rumor.action) {
case 'kill':
if (rumor.targetType === this.type) {
/**
* If the type that was killed was the same as
* the current `Entity`'s type, reputation drop by
* 0.1.
*/
this.reputation = Math.max(-1, this.reputation - 0.1);
this.converse('That person sucks!');
} else if (this.dislike.includes(rumor.targetType)) {
/**
* If the current entity dislikes the type of entity
* that was killed,
* rep increases by 0.1
*/
this.reputation = Math.min(1, this.reputation + 0.25);
this.converse('I LOVE THAT PERSON!');
}
}
};
Entity.prototype.converse = function(text) {
let chat = game.add.text(32, 0, text);
chat.anchor.setTo(0.5);
chat.font = 'Press Start 2P';
chat.fill = '#ffff00';
chat.fontSize = '1.5em';
chat.stroke = 'black';
chat.strokeThickness = '4';
chat.align = 'center';
chat.lifespan = 3000; // milliseconds
this.addChild(chat);
};
/**
* Entity module.
* @module: entity/Entity
*/
module.exports = Entity;