UNPKG

elpy

Version:
642 lines (506 loc) 16.3 kB
const point = require('./Point'); class EngineObject { constructor(name, x, y, width, height, options = {}) { this._name = name; this._x = x; this._y = y; this._width = width; this._height = height; this._events = {}; this._collision = {}; this._isJumping = false; this._isFalling = false; this._isFlying = false; this._isStopped = false; this._state = null; this._ghost = false; this._animate = false; this._added = false, this._exist = true; this._track = { x: null, y: null }; this._dest = { x: null, y: null }; this._offset = { x: 0, y: 0, object: null, rotate: { x: 0, y: 0 } }; this._positions = { start: { x: null, y: null } } this._degrees = 0; this._options = { obstacle: typeof options.obstacle === 'boolean' ? options.obstacle : true, pushing: typeof options.pushing === 'boolean' ? options.pushing : false, disabledEvents: typeof options.disabledEvents === 'boolean' ? options.disabledEvents : false, type: options.type || null, custom: options.custom || null, color: options.color || 'black', image: { path: (typeof options.image === 'object' && options.image !== null ? options.image.path : options.image) || null, repeat: (typeof options.image === 'object' && options.image !== null ? options.image.repeat : false) || false, rendering: false, cached: null }, images: { list: options.images || null, rendering: false, cached: {} }, fixedCamera: { x: false, y: false } }; this._params = { movement: { acceleration: 0 }, jump: { multiplier: 0 }, fall: { multiplier: 0 } } this._MAX_ACCELERATION = 10; this._init(); } run(step = 1) { const x = this._x + Math.cos((this._degrees + 90) * Math.PI / 180) * step; const y = this._y + Math.sin((this._degrees + 90) * Math.PI / 180) * step; this.move(x, y); } move(x, y) { this._dest.x = x; this._dest.y = y; this._track.x = this._x; this._track.y = this._y; for(const name in this._collision) { const object = this._collision[name]; if (this !== object && this.isExist && object.isExist && !object.ghost && (x + this._width) > object.x && x < (object.x + object.width) && (y + this._height) > object.y && y < (object.y + object.height)) { const side = this._getCollisionSide(x, y, object); this._dispatchEvent('collision', object, side); if (object.options.obstacle) { return false; } } } if (this._options.fixedCamera.x && x > this._track.x) { this._offset.x += Math.abs(this._track.x - x); } if (this._options.fixedCamera.x && x < this._track.x) { this._offset.x -= Math.abs(this._track.x - x); } if (this._options.fixedCamera.y && y > this._track.y) { this._offset.y += Math.abs(this._track.y - y); } if (this._options.fixedCamera.y && y < this._track.y) { this._offset.y -= Math.abs(this._track.y - y); } this._x = x; this._y = y; this._dispatchEvent('move'); } fly(degrees = 0, distance = 0, step = 1) { const event = this._getEventObject(); this._positions.start.x = this._x; this._positions.start.y = this._y; this._nextTick(() => { this._isStopped = false; this._isFlying = true; this._tick(this._onFly.bind(this, event, degrees, distance, step)); }); } jump(height = 0, multiplier = 0.1, forced = false) { const event = this._getEventObject(); if (forced) { this._isJumping = false; } if (this._isJumping) { return false; } this._isFalling = false; this._isJumping = true; this._params.movement.acceleration = this._getMaxJumpAccelerationValue(height, multiplier); this._params.jump.multiplier = multiplier; this._nextTick(() => { this._isStopped = false; this._tick(this._onJump.bind(this, event)); }); } fall(multiplier = 0.1) { const event = this._getEventObject(); this._isFalling = true; this._params.fall.multiplier = multiplier; this._nextTick(() => { this._isStopped = false; this._tick(this._onFall.bind(this, event)); }); } push(pusher) { let direction; let distance; if (pusher.dest.y < pusher.y) { direction = 'up'; distance = Math.abs(pusher.dest.y - pusher.y); } if (pusher.dest.y > pusher.y) { direction = 'down'; distance = Math.abs(pusher.dest.y - pusher.y); } if (pusher.dest.x > pusher.x) { direction = 'right'; distance = Math.abs(pusher.dest.x - pusher.x); } if (pusher.dest.x < pusher.x) { direction = 'left'; distance = Math.abs(pusher.dest.x - pusher.x); } switch (direction) { case 'up': this.move(this.x, this.y - distance); if (this.track.y !== this.y) { pusher.move(pusher.x, pusher.y - distance); } break; case 'down': this.move(this.x, this.y + distance); if (this.track.y !== this.y) { pusher.move(pusher.x, pusher.y + distance); } break; case 'right': this.move(this.x + distance, this.y); if (this.track.x !== this.x) { pusher.move(pusher.x + distance, pusher.y); } break; case 'left': this.move(this.x - distance, this.y); if (this.track.x !== this.x) { pusher.move(pusher.x - distance, pusher.y); } break; } } rotate(degrees = 0, x = 0, y = 0) { this._offset.rotate.x = x; this._offset.rotate.y = y; this._degrees = degrees; this._dispatchEvent('rotate'); } stop() { this._isStopped = true; } destroy() { delete this._collision[this._name]; this._exist = false; this._dispatchEvent('destroy'); } collision(object) { if (Array.isArray(object)) { object.forEach(item => { if (item.isExist) { this._collision[item.name] = item; } }); } else { if (object.isExist) { this._collision[object.name] = object; } } } on(name, callback) { if (!this._events[name]) { this._events[name] = []; } this._events[name].push(callback); } setOffsetObject(object) { this._offset.object = object; } removeCollision(object) { delete this._collision[object.name]; } get name() { return this._name; } get options() { return this._options; } get obstacles() { return Object.values(this._collision); } get track() { return this._track; } get dest() { return this._dest; } get offset() { return this._offset; } get isPushing() { return this._options.pushing; } get isJumping() { return this._isJumping; } get isFalling() { return this._isFalling; } get isFlying() { return this._isFlying; } get isExist() { return this._exist; } get x() { return this._x; } set x(value) { this._x = value; } get y() { return this._y; } set y(value) { this._y = value; } get width() { return this._width; } set width(value) { return this._width = value; } get height() { return this._height; } set height(value) { return this._height = value; } get state() { return this._state; } set state(state) { this._state = state; this._dispatchEvent('state'); } get animate() { return this._animate; } set animate(value) { if (value) { if (Array.isArray(this.options.images.list) && this.options.images.list.length > 0) { requestAnimationFrame(this._animation.bind(this)); } } this._animate = value; } get ghost() { return this._ghost; } set ghost(value) { this._ghost = value; } get degrees() { return this._degrees; } get added() { return this._added; } set added(value) { return this._added = value; } get _isAccelerationMovementStopped() { return this._params.movement.acceleration <= 0; } _dispatchEvent(name, ...data) { if (this._events[name] && Array.isArray(this._events[name]) && this._events[name].length > 0) { this._events[name].forEach(callback => { callback(...data); }); } } _animation() { if (this._animate) { this._dispatchEvent('state'); requestAnimationFrame(this._animation.bind(this)); } } _takeoff() { const acceleration = Math.floor(this._params.movement.acceleration * 10); const multiplier = this._params.jump.multiplier * 10; this._params.movement.acceleration = (acceleration - multiplier) / 10; this.move(this._x, Math.floor(this._y - this._params.movement.acceleration)); } _landing() { if (this._params.movement.acceleration <= this._MAX_ACCELERATION) { const acceleration = Math.floor(this._params.movement.acceleration * 10); const multiplier = this._params.fall.multiplier * 10; this._params.movement.acceleration = (acceleration + multiplier) / 10; } const moving = this.move(this._x, Math.floor(this._y + this._params.movement.acceleration)); if (moving === false) { this._params.movement.acceleration = 0; return true; } } _getCollisionSide(x, y, object) { let side = null; if ((x + this._width) > object.x && x < (object.x + object.width) && ((this._y + this._height) <= object.y || this._y >= (object.y + object.height))) { const top = object.y - (y + this._height); const bottom = y - (object.y + object.height); if (top > bottom) { side = 'top'; } else { side = 'bottom'; } } if ((y + this._height) > object.y && y < (object.y + object.height) && ((this._x + this._width) <= object.x || this._x >= (object.x + object.width))) { const left = object.x - (x + this._width); const right = x - (object.x + object.width); if (left > right) { side = 'left'; } else { side = 'right'; } } return side; } _getMaxJumpAccelerationValue(max, multiplier) { let min = 0; let acceleration = 0; while(min <= max) { min += acceleration; acceleration = (Math.floor(acceleration * 10) + (multiplier * 10)) / 10; } return acceleration; } _onCollisionSide(object, side) { if (object.options.obstacle && side === 'bottom') { this._params.movement.acceleration = 0; } } _tick(callback) { const response = callback(); if (response !== false) { requestAnimationFrame(this._tick.bind(this, callback)); } } _nextTick(callback) { requestAnimationFrame(callback); } _getEventObject() { const event = { _stopped: false, _paused: false, get stopped() { return this._stopped; }, get paused() { return this._paused; }, stop() { this._stopped = true; }, pause() { this._paused = true; }, resume() { this._paused = false; } } return event; } _onJump(event) { if (this._isStopped) { return false; } if (event.paused) { this._dispatchEvent('jump', event); return; } if (event.stopped) { this._isFalling = true; return false; } if (this._isAccelerationMovementStopped) { this._isFalling = true; return false; } else { this._takeoff(); this._dispatchEvent('jump', event); } } _onFall(event) { if (this._isStopped) { return false; } if (event.paused) { this._dispatchEvent('fall', event); return; } if (event.stopped) { this._isJumping = false; return false; } if (this._isFalling) { const landed = this._landing(); if (landed === true) { this._isJumping = false; } this._dispatchEvent('fall', event); } } _onFly(event, degrees, distance, step) { if (this._isStopped) { this._isFlying = false; return false; } if (event.paused) { this._dispatchEvent('fly', event); return; } if (event.stopped) { this._isFlying = false; return false; } if (distance > 0 && point.distance(this._positions.start.x, this._positions.start.y, this._x, this._y) > distance) { this.destroy(); return false; } let x = this._x + parseFloat(Math.cos(degrees * Math.PI / 180).toFixed(10)) * step; let y = this._y + parseFloat(Math.sin(degrees * Math.PI / 180).toFixed(10)) * step; this.move(x, y); this._dispatchEvent('fly', event); } _init() { this.on('collision', this._onCollisionSide.bind(this)); } } module.exports = EngineObject;