@fuwu-yuan/bgew
Version:
Baguette Game Engine Web is a french 2D Web game engine
523 lines (518 loc) • 16.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Entity = void 0;
const Dispatcher_1 = require("../classes/Dispatcher");
const entities_1 = require("./entities");
const collisionSystem_1 = require("../collisionSystem");
/**
* The most important part of your game
* To build a game, you need to create entities and add it to your board
*
*/
class Entity {
constructor(x, y, width, height, id = "") {
this._parent = null;
this._rotate = 0;
this._zoom = 1;
//private _directionX: number = 0;
//private _directionY: number = 0;
//private _speed: number = 0;
this._speedX = 0;
this._speedY = 0;
this._dispatcher = new Dispatcher_1.Dispatcher();
this._board = null;
this._visible = true;
this.hovered = false;
this._disabled = false;
this._focus = false;
this._solid = false;
this._weight = 0;
this._gravitySpeed = 0;
this._falling = true;
this._opacity = 1;
this._id = id !== "" ? id : ('@entity-' + Entity.__AI++);
this._x = x;
this._y = y;
this._width = width;
this._height = height;
this._body = new collisionSystem_1.Point(this);
}
init(board) {
var _a;
this.board = board;
this._body = new collisionSystem_1.Polygon(this, this.absX, this.absY, [
[0, 0],
[this.width, 0],
[this.width, this.height],
[0, +this.height]
]);
if ((_a = this.board) === null || _a === void 0 ? void 0 : _a.collisionSystem) {
this.board.collisionSystem.insert(this.body);
}
}
get parent() {
return this._parent;
}
set parent(value) {
this._parent = value;
}
get board() {
return this._board;
}
set board(value) {
this._board = value;
}
get rotate() {
return this._rotate;
}
set rotate(angle) {
angle = angle * Math.PI / 180;
this._rotate = angle;
}
get zoom() {
return this._zoom;
}
set zoom(value) {
this._zoom = value;
}
get id() {
return this._id;
}
/**
* BE AWARE : if you change entity id after registered event listeners, events will not be triggered anymore for this entity, you will need to remove event listeners and register it again.
* @param id
*/
set id(id) {
this._id = id;
}
get x() {
return this._x;
}
set x(value) {
this._x = value;
}
get y() {
return this._y;
}
set y(value) {
this._y = value;
}
/*get speedX(): number {
return this.speed * this.directionX;
}
get speedY(): number {
return this.speed * this.directionY;
}
get speed(): number {
return this._speed;
}
set speed(value: number) {
this._speed = value;
}*/
/**
* Get directionX in radian
*/
/*get directionX(): number {
return this._directionX;
}*/
/**
* Set directionX in radian
* @param value in radian
*/
/*set directionX(value: number) {
this._directionX = value;
}*/
/**
* Get directionY in radian
*/
/*get directionY(): number {
return this._directionY;
}*/
/**
* Set directionY in radian
* @param value in radian
*/
/*set directionY(value: number) {
this._directionY = value;
}*/
/**
* Get direction in radian<br>
* To get degrees use {@link Entity.directionDegrees}
*/
/*get direction() {
return Math.atan(this.directionY / this.directionX);
}*/
/**
* Set direction in radian<br>
* To use degrees use {@link Entity.directionDegrees}
* @param value in radian
*/
/*set direction(value: number) {
this.directionX = Math.cos(value);
this.directionY = Math.sin(value);
}*/
/**
* Get the direction in degrees
*/
/*get directionDegrees() {
return this.direction * 180 / Math.PI;
}*/
/**
* Set the direction in degrees
* @param value in degrees
*/
/*set directionDegrees(value: number) {
this.direction = value / 180 * Math.PI;
}*/
/**
* Get entity speedX
*/
get speedX() {
return this._speedX;
}
/**
* Set entity speedX
* @param value
*/
set speedX(value) {
this._speedX = value;
}
/**
* Get entity speedY
*/
get speedY() {
return this._speedY;
}
/**
* Set entity speedY
* @param value
*/
set speedY(value) {
this._speedY = value;
}
/**
*
* Set entity speed in specified angle in radian or degrees
*
* @param speed in pixel per seconds
* @param angle in radian or degrees
* @param degrees true if angle is in degrees (default: radian)
*/
setSpeedWithAngle(speed, angle, degrees = false) {
if (degrees) {
angle = angle / 180 * Math.PI;
}
this._speedX = speed * Math.cos(angle);
this._speedY = -speed * Math.sin(angle);
}
/**
* Set entity speed keeping current angle (default angle : atan2(-0, 0) = 0 rad)
* @param value
*/
set speed(value) {
this.setSpeedWithAngle(value, this.angle);
}
/**
* Get current speed (depending on speedX and speedY)
*/
get speed() {
return Math.sqrt(this.speedX * this.speedX + this.speedY * this.speedY);
}
/**
* Get current angle in radian (depending on speedX and speedY). Default angle : atan2(-0, 0) = 0 rad
*/
get angle() {
return Math.atan2(-this.speedY, this.speedX);
}
/**
* Set angle in radian keeping current speed (will update speedX and speedY) keeping global speed
*
* @param value
*/
set angle(value) {
this.setSpeedWithAngle(this.speed, value);
}
/**
* Get current angle in degrees
*/
get angleInDegrees() {
return this.angle * 180 / Math.PI;
}
/**
* Set angle in degrees keeping current speed (will update speedX and speedY) keeping global speed
* @see Entity.angle
* @param value
*/
set angleInDegrees(value) {
this.setSpeedWithAngle(this.speed, value, true);
}
get absX() {
return this.x + (this.parent ? this.parent.absX : 0);
}
get absY() {
return this.y + (this.parent ? this.parent.absY : 0);
}
get height() {
return this._height * this._zoom;
}
set height(value) {
this._height = value;
}
get width() {
return this._width * this._zoom;
}
set width(value) {
this._width = value;
}
get visible() {
return this._visible && (!this.parent || this.parent.visible);
}
set visible(value) {
this._visible = value;
}
get dispatcher() {
return this._dispatcher;
}
get solid() {
return this._solid;
}
set solid(value) {
this._solid = value;
}
get weight() {
return this._weight;
}
set weight(value) {
this._weight = value;
}
get falling() {
return this._falling;
}
set falling(value) {
this._falling = value;
}
intersect(x, y, event = null, depth = 1) {
var _a, _b, _c, _d, _e, _f, _g;
// Rotate context from entity center
(_a = this.board) === null || _a === void 0 ? void 0 : _a.ctx.translate(this.x + this.width / 2, this.y + this.height / 2);
(_b = this.board) === null || _b === void 0 ? void 0 : _b.ctx.rotate(this.rotate);
(_c = this.board) === null || _c === void 0 ? void 0 : _c.ctx.translate((this.x + this.width / 2) * -1, (this.y + this.height / 2) * -1);
let result = ((_d = this.board) === null || _d === void 0 ? void 0 : _d.ctx.isPointInPath(this.getPath2D(), x, y)) || false;
// UNDO Rotate context from entity center
(_e = this.board) === null || _e === void 0 ? void 0 : _e.ctx.translate(this.x + this.width / 2, this.y + this.height / 2);
(_f = this.board) === null || _f === void 0 ? void 0 : _f.ctx.rotate(this.rotate * -1);
(_g = this.board) === null || _g === void 0 ? void 0 : _g.ctx.translate((this.x + this.width / 2) * -1, (this.y + this.height / 2) * -1);
return result;
}
intersectWithEntity(entity) {
var _a, _b;
if ((_a = this.board) === null || _a === void 0 ? void 0 : _a.collisionResult) {
return this.body.collides(entity.body, (_b = this.board) === null || _b === void 0 ? void 0 : _b.collisionResult);
}
return false;
}
/**
* Check if entity intersect with any of the given entities
* @param entities Entities to check collision
* @return An array containing intersecting entities
*/
intersectWithEntities(entities) {
let intersect = [];
if (this.board) {
for (const entity of entities) {
if (entity === this)
continue;
if (this.intersectWithEntity(entity)) {
if (entity instanceof entities_1.Container) {
intersect = intersect.concat(this.intersectWithEntities(entity.entities));
}
intersect.push(entity);
}
}
}
return intersect;
}
/**
* Listen mouse event on this entity
* @param event An event from this list : click, dblclick, contextmenu, mousedown, mouseup, mouseenter, mouseleave, mousemove, all
* @param callback
* @param options
*/
onMouseEvent(event, callback, options) {
this.dispatcher.on(event, callback, options);
}
/**
* Listen mouse event on this entity
* @param event An event from this list : keyup, keydown, keypress, all
* @param callback
* @param options
*/
onKeyboardEvent(event, callback, options) {
this.dispatcher.on(event, callback, options);
}
/**
* Listen for entity intersect with x and y
* @param x
* @param y
* @param callback
* @param options
*/
onIntersect(x, y, callback, options) {
this.dispatcher.on(`intersect-${x}-${y}`, callback, options);
}
/**
* Listen for the two entities intersection
* @param entity
* @param callback
* @param options
*/
onIntersectWithEntity(entity, callback, options) {
this.dispatcher.on(`entityintersect-${entity.id}`, callback, options);
}
onIntersectWithAnyEntity(callback, options) {
this.dispatcher.on("anyentityintersect", callback, options);
}
getPath2D() {
let path = new Path2D();
path.rect(this.x, this.y, this.width, this.height);
path.closePath();
return path;
}
get body() {
return this._body;
}
get opacity() {
return this._opacity;
}
set opacity(value) {
this._opacity = value;
}
debug(ctx) {
if (this.board) {
if (this.board.debug.skeleton) {
this.board.ctx.strokeStyle = "red";
this.board.ctx.stroke(this.getPath2D());
}
}
}
reset() {
this.x = 0;
this.y = 0;
this.rotate = 0;
this.zoom = 1;
}
draw(ctx) {
if (this.board) {
ctx.globalAlpha = this.opacity;
ctx.scale(this.board.scale, this.board.scale);
}
}
onDestroy() {
}
update(delta) {
this._body.x = this.absX;
this._body.y = this.absY;
this._body.scale_x = this.zoom;
this._body.scale_y = this.zoom;
this.updateGravity(delta);
this.updateSpeed(delta);
}
updateGravity(delta) {
if (this.board && this.board.gravity > 0 && this._weight > 0) {
this.falling = true;
this.speedY += this.board.gravity * delta / 1000;
if (this.speedY > 0 && this.solid) {
let willY = this.absY + this.height + (this.speedY * delta / 1000) + 1;
let willHit = this.board.getEntitiesIn(this.absX + 1, willY, this.absX + this.width - 1, willY).find((e) => { return e.solid; });
if (typeof willHit !== 'undefined' && willHit.absY <= this.absY + this.height) {
this.speedY = 0;
this.y = willHit.y - this.height;
this.falling = false;
}
}
}
}
/*private getCollisionBellowOverlap(delta: number) {
let collideBellowOverlap = null;
if (this.board) {
//this.body.y += this.board?.gravity*delta/1000;
//this.board.collisionSystem.update();
const potentials = this.body.potentials();
for (const body of potentials) {
if (this.body.collides(body, this.board.collisionResult)
&& this.board.collisionResult.overlap_y === 1
&& this.solid
&& (body as Circle|Polygon|Point).entity?.solid) {
collideBellowOverlap = this.board.collisionResult.overlap * this.board.collisionResult.overlap_y;
break;
}
}
//this.body.y -= this.board?.gravity*delta/1000;
//this.board.collisionSystem.update();
return collideBellowOverlap;
}
}
private updateGravity(delta: number) {
if (this.board && this.board.gravity > 0 && this._weight > 0) {
let collisionOverlapY = this.getCollisionBellowOverlap(delta);
if (!collisionOverlapY) {
this.falling = true;
this.speedY += this.board?.gravity*delta/1000;
}else if (this.speedY > 0) {
this.falling = false;
this.speedY = 0;
this.y -= collisionOverlapY-1;
}
}
}*/
updateSpeed(delta) {
this.x += this.speedX * delta / 1000;
this.y += this.speedY * delta / 1000;
}
checkCollisions() {
if (this.board) {
for (const event of Object.keys(this.dispatcher.events)) {
// Intersection with point
if (event.startsWith("intersect-")) {
let params = event.split("-");
let x = parseInt(params[1]);
let y = parseInt(params[2]);
if (this.intersect(x, y)) {
this.dispatcher.dispatch(event, /*entity:*/ this, /*point:*/ { x: x, y: y });
}
// Intersection with entity
}
else if (event.startsWith("entityintersect-")) {
let params = event.split("-");
let otherEntityId = params.slice(1).join('-');
if (this.board) {
let otherEntity = this.board.findEntity(otherEntityId);
if (typeof otherEntity !== 'undefined') {
if (this.intersectWithEntity(otherEntity)) {
this.dispatcher.dispatch(event, /*entity:*/ this, /*collisionWith:*/ otherEntity, /*result:*/ this.board.collisionResult);
}
}
}
// Intersection with any entity
}
else if (event === "anyentityintersect" && this.board.collisionResult) {
const potentials = this.body.potentials();
for (const body of potentials) {
if (this.body.collides(body, this.board.collisionResult)) {
this.dispatcher.dispatch(event, /*entity:*/ this, /*collisionWith:*/ body.entity, /*result:*/ this.board.collisionResult);
}
}
}
}
}
}
/*********************
* Getters & Setters *
*********************/
get disabled() { return this._disabled; }
set disabled(value) { this._disabled = value; }
get focus() { return this._focus; }
set focus(value) { this._focus = value; }
}
exports.Entity = Entity;
Entity.__AI = 0;