261 lines
7.2 KiB
JavaScript
261 lines
7.2 KiB
JavaScript
class AgentController {
|
|
constructor(canvas) {
|
|
// Inicjalizacja
|
|
this.ctx = canvas.getContext('2d');
|
|
this.setCanvasSize(canvas);
|
|
|
|
this.ownedProduct = Product.REGISTRY.empty;
|
|
this.ownedProductAmount = 0;
|
|
|
|
// Akcje
|
|
this.actions = {
|
|
'STATIONARY': 0,
|
|
'IN_MOVE': 1,
|
|
'IN_ROTATION': 2,
|
|
}
|
|
this.currentAction = this.actions.STATIONARY;
|
|
|
|
// Pozycja i poruszanie się
|
|
this.position = { x: 6, y: 0 };
|
|
this.rotation = { z: 0 };
|
|
this.speed = 90 - AgentMoveSpeed;
|
|
this.rotationSpeed = 90 / ( 90 - AgentRotationSpeed);
|
|
this.update();
|
|
}
|
|
|
|
setCanvasSize (canvas) {
|
|
canvas.width = 2000;
|
|
canvas.height = 900;
|
|
}
|
|
|
|
update() {
|
|
this.clearCanvas();
|
|
if (this.currentPath) {
|
|
this.drawPath(this.currentPath);
|
|
}
|
|
this.drawAgent();
|
|
|
|
requestAnimationFrame(this.update.bind(this));
|
|
}
|
|
|
|
updateAgentPosition() {
|
|
return new Promise((resolve, reject) => {
|
|
const cycle = async () => {
|
|
if (!this.currentPathIndex) {
|
|
this.currentPathIndex = 0;
|
|
}
|
|
|
|
let nearestPoint = this.currentPath[this.currentPathIndex];
|
|
|
|
if (nearestPoint) {
|
|
if (nearestPoint.action == PATHFINDING_ACTION.MOVE) {
|
|
await this.moveInLine(nearestPoint.state.position);
|
|
}
|
|
if (nearestPoint.action == PATHFINDING_ACTION.ROTATE) {
|
|
await this.rotate({ z: nearestPoint.state.rotation });
|
|
}
|
|
|
|
nearestPoint = this.currentPath[++this.currentPathIndex];
|
|
if (!nearestPoint) {
|
|
this.currentPath = [];
|
|
this.currentPathIndex = 0;
|
|
this.currentAction = this.actions.STATIONARY;
|
|
resolve();
|
|
} else {
|
|
cycle();
|
|
}
|
|
} else {
|
|
resolve();
|
|
}
|
|
}
|
|
|
|
cycle();
|
|
})
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {{ x: number, y: number }} position
|
|
*/
|
|
moveInLine(position) {
|
|
return new Promise((resolve, reject) => {
|
|
const cycle = () => {
|
|
const { speed } = this;
|
|
this.currentAction = this.actions.IN_MOVE;
|
|
this.position = {
|
|
x: this.position.x + (Math.sign(position.x - this.position.x)/speed),
|
|
y: this.position.y + (Math.sign(position.y - this.position.y)/speed)
|
|
}
|
|
|
|
if (Math.abs(position.x - this.position.x) < 0.001 && Math.abs(position.y - this.position.y) < 0.001) {
|
|
this.position = {
|
|
x: Math.round(this.position.x),
|
|
y: Math.round(this.position.y)
|
|
}
|
|
resolve();
|
|
} else {
|
|
requestAnimationFrame(() => cycle());
|
|
}
|
|
}
|
|
|
|
cycle();
|
|
});
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {{ x: number, y: number }} position
|
|
*/
|
|
findRequiredRotation(position) {
|
|
if (position.x == this.position.x && position.y == this.position.y) {
|
|
return this.rotation.z;
|
|
}
|
|
|
|
const compare = (a, b) => {
|
|
return Math.abs(a - b) < 1;
|
|
}
|
|
|
|
if (compare(this.position.x, position.x)) {
|
|
if (this.position.y < position.y) {
|
|
return 180;
|
|
} else {
|
|
return 0;
|
|
}
|
|
} else {
|
|
if (this.position.x < position.x) {
|
|
return 90;
|
|
} else {
|
|
return 270;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {{ z: number}} rotation
|
|
* @returns
|
|
*/
|
|
rotate(rotation) {
|
|
return new Promise((resolve, reject) => {
|
|
if (this.rotation.z == rotation.z) {
|
|
resolve();
|
|
}
|
|
|
|
const sign = (() => {
|
|
if (((this.rotation.z > rotation.z) || (this.rotation.z == 0 && rotation.z == 270)) && !(this.rotation.z == 270 && rotation.z == 0)) {
|
|
return -1;
|
|
}
|
|
return 1;
|
|
})();
|
|
|
|
const cycle = () => {
|
|
this.currentAction = this.actions.IN_ROTATION;
|
|
|
|
const oldZRotation = this.rotation.z;
|
|
const newZRotation = rotation.z;
|
|
|
|
const clampRotation = (rot) => (rot + 360) % 360;
|
|
|
|
this.rotation = { z: clampRotation(oldZRotation + (this.rotationSpeed * sign)) };
|
|
|
|
if (Math.abs(oldZRotation - newZRotation) < 5) {
|
|
resolve();
|
|
this.rotation = { z: clampRotation(newZRotation) }
|
|
} else {
|
|
requestAnimationFrame(() => cycle());
|
|
}
|
|
}
|
|
|
|
cycle();
|
|
});
|
|
}
|
|
|
|
moveTo(x, y) {
|
|
this.currentPath = this.findPathTo(x, y, this.position.x, this.position.y);
|
|
return this.updateAgentPosition();
|
|
}
|
|
|
|
drawPath(path) {
|
|
for(let i = 0; i < path.length - 1; i++) {
|
|
this.ctx.beginPath();
|
|
this.ctx.strokeWidth = 5;
|
|
this.ctx.strokeStyle = 'red';
|
|
this.ctx.moveTo(path[i].state.position.x * 100 + 50, path[i].state.position.y * 100 + 50);
|
|
this.ctx.lineTo(path[i + 1].state.position.x * 100 + 50, path[i + 1].state.position.y * 100 + 50);
|
|
this.ctx.stroke();
|
|
}
|
|
}
|
|
|
|
drawAgent() {
|
|
const {x, y} = this.position;
|
|
const offset = 5;
|
|
const rotation = this.rotation.z;
|
|
|
|
const newX = (x * 100) + (offset);
|
|
const newY = (y * 100) + (offset);
|
|
const w = 100 - (offset * 2);
|
|
const h = 100 - (offset * 2);
|
|
|
|
this.ctx.translate(newX + (w / 2), newY + (h / 2));
|
|
this.ctx.rotate(rotation * Math.PI / 180);
|
|
|
|
// Base shape
|
|
this.ctx.clearRect(-(w / 2), -(h / 2), w, h);
|
|
this.roundRect(-(w / 2), -(h / 2), w, h);
|
|
|
|
// Eyes
|
|
// this.roundRect(10 - (w / 2), 10 - (h / 2), w / 4, h / 4);
|
|
// this.roundRect(80 - (3 * w / 4), 10 - (h / 2), w / 4, h / 4);
|
|
this.ctx.fillStyle = 'red';
|
|
this.ctx.fillRect(-(w/2) + 20, (-h/2) - 10, w - 40, 10);
|
|
|
|
this.ctx.rotate(-rotation * Math.PI / 180);
|
|
|
|
// Product
|
|
this.ctx.font = '900 40px "Font Awesome 5 Free"';
|
|
this.ctx.textAlign = 'center';
|
|
this.ctx.fillStyle = 'white';
|
|
this.ctx.fillText(this.ownedProduct.icon, 0, 15);
|
|
|
|
// Amount
|
|
if (this.ownedProductAmount) {
|
|
this.ctx.font = '20px Montserrat';
|
|
this.ctx.textAlign = 'left';
|
|
this.ctx.fillStyle = 'white';
|
|
this.ctx.fillText(this.ownedProductAmount, 10, 35);
|
|
}
|
|
|
|
this.ctx.translate(-(newX + (w / 2)), -(newY + (h / 2)));
|
|
}
|
|
|
|
findPathTo(dx, dy, sx, sy) {
|
|
if (Grid.instance.getGrid()[dx][dy] == GRID_FIELD_TYPE.SHELF || Grid.instance.getGrid()[dx][dy] == GRID_FIELD_TYPE.STORAGE) {
|
|
if (Grid.instance.getGrid()[dx][dy - 1] == GRID_FIELD_TYPE.PATH) {
|
|
return Pathfinding.search({ position: { x: sx, y: sy }, rotation: this.rotation.z }, { position: { x: dx, y: dy - 1 }, rotation: 180 });
|
|
}
|
|
return Pathfinding.search({ position: { x: sx, y: sy }, rotation: this.rotation.z }, { position: { x: dx, y: dy + 1 }, rotation: 0 });
|
|
}
|
|
return Pathfinding.search({ position: { x: sx, y: sy }, rotation: this.rotation.z }, { position: { x: dx, y: dy }, rotation: this.rotation.z });
|
|
}
|
|
|
|
clearCanvas() {
|
|
this.ctx.clearRect(0, 0, 2000, 900);
|
|
}
|
|
|
|
roundRect(x, y, w, h, r = 20) {
|
|
this.ctx.lineWidth = 5;
|
|
this.ctx.strokeStyle = 'red';
|
|
this.ctx.beginPath();
|
|
this.ctx.moveTo(x + r, y);
|
|
this.ctx.lineTo(x + w - r, y);
|
|
this.ctx.quadraticCurveTo(x + w, y, x + w, y + r);
|
|
this.ctx.lineTo(x + w, y + h - r);
|
|
this.ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
|
|
this.ctx.lineTo(x + r, y + h);
|
|
this.ctx.quadraticCurveTo(x, y + h, x, y + h - r);
|
|
this.ctx.lineTo(x, y + r);
|
|
this.ctx.quadraticCurveTo(x, y, x + r, y);
|
|
this.ctx.closePath();
|
|
this.ctx.stroke();
|
|
}
|
|
} |