psi/src/view/agentController.js

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();
}
}