182 lines
5.7 KiB
TypeScript
182 lines
5.7 KiB
TypeScript
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
// Author: Sascha Nitsch https://contentnation.net/@grumpydevelop
|
|
|
|
import { WorldSim } from './worldsim';
|
|
import { RobotCmd } from './robotcmd';
|
|
import { RobotStatusExt } from './robotstatus';
|
|
|
|
class Robot {
|
|
private name: string;
|
|
private model: string;
|
|
private status: RobotStatusExt;
|
|
private cmd: RobotCmd;
|
|
private world: WorldSim;
|
|
private uuid: string;
|
|
private width: number;
|
|
private height: number;
|
|
private fireCooldown: number;
|
|
private topSpeed: number = 20;
|
|
private radarRange: number = 200;
|
|
|
|
constructor(worldsim: WorldSim, uuid: string, width: number, height: number) {
|
|
this.world = worldsim;
|
|
this.uuid = uuid;
|
|
this.name = '';
|
|
this.model = '';
|
|
this.status = new RobotStatusExt();
|
|
this.width = width;
|
|
this.height = height;
|
|
this.status.posX = Math.random()*(width - 20) + 10,
|
|
this.status.posY = Math.random()*(height - 20) + 10,
|
|
this.status.orientation = Math.random()*360;
|
|
this.fireCooldown = 0;
|
|
this.cmd = new RobotCmd;
|
|
this.cmd.mergeObject({
|
|
powerLeft: 0,
|
|
powerRight: 0,
|
|
radarMin: 0,
|
|
radarMax: 360,
|
|
gunTarget: 0,
|
|
fire: false
|
|
});
|
|
}
|
|
|
|
causedDamage(amount: number) {
|
|
this.status.causedDamage = amount;
|
|
}
|
|
damage(amount: number) {
|
|
this.status.health -= amount;
|
|
}
|
|
|
|
getStatus() : RobotStatusExt {
|
|
return this.status;
|
|
}
|
|
|
|
getX() : number {
|
|
return this.status.posX;
|
|
}
|
|
|
|
getY() : number {
|
|
return this.status.posY;
|
|
}
|
|
|
|
isReady() : boolean {
|
|
return this.name !== '' && this.model !== '';
|
|
}
|
|
|
|
moveRobot(dt: number) {
|
|
if (this.cmd.fire && this.fireCooldown <= 0) {
|
|
this.world.fire(this.uuid, this.status.posX, this.status.posY, this.status.orientation + this.status.gunOrient);
|
|
this.fireCooldown = 10; // 10 seconds to reload
|
|
}
|
|
if (this.fireCooldown > 0) {
|
|
this.fireCooldown -= dt;
|
|
}
|
|
this.status.chainSpeedLeft += (0.3*(this.cmd.powerLeft-this.status.chainSpeedLeft));
|
|
this.status.chainSpeedRight += (0.3*(this.cmd.powerRight-this.status.chainSpeedRight));
|
|
const speed = (this.status.chainSpeedLeft + this.status.chainSpeedRight)/2.0;
|
|
this.status.posX += speed * Math.sin(this.status.orientation * Math.PI / 180.0) * dt * this.topSpeed;
|
|
this.status.posY -= speed * Math.cos(this.status.orientation * Math.PI / 180.0) * dt * this.topSpeed;
|
|
if (this.status.posX < 0) {
|
|
this.status.posX = 0;
|
|
this.damage(1);
|
|
}
|
|
if (this.status.posY < 0) {
|
|
this.status.posY = 0;
|
|
this.damage(1);
|
|
}
|
|
if (this.status.posX > this.width) {
|
|
this.status.posX = this.width;
|
|
this.damage(1);
|
|
}
|
|
if (this.status.posY > this.height) {
|
|
this.status.posY = this.height;
|
|
this.damage(1);
|
|
}
|
|
|
|
this.status.orientation += (this.status.chainSpeedLeft - this.status.chainSpeedRight) * 30 * dt;
|
|
if (this.status.orientation >= 360.0) this.status.orientation -= 360.0;
|
|
if (this.status.orientation < 0.0) this.status.orientation += 360.0;
|
|
if (this.cmd.radarMax == this.cmd.radarMin && this.cmd.radarMax == -1) { // full 360
|
|
this.status.radarPos += 60 * dt; // 60 degree per sec
|
|
if (this.status.radarPos > 360) this.status.radarPos -= 360;
|
|
} else if (this.status.radarInc) {
|
|
this.status.radarPos = (this.status.radarPos + 60 * dt) % 360;
|
|
if (Math.abs(this.cmd.radarMax - this.status.radarPos) < 30 && this.cmd.radarMax <= this.status.radarPos) {
|
|
this.status.radarInc = false;
|
|
}
|
|
} else {
|
|
this.status.radarPos = (360 + this.status.radarPos - 60 * dt) % 360;
|
|
if (Math.abs(this.cmd.radarMin - this.status.radarPos)< 30 && this.cmd.radarMin < this.status.radarPos) {
|
|
this.status.radarInc = true;
|
|
}
|
|
}
|
|
if (Math.abs(this.status.gunOrient - this.cmd.gunTarget) > 0.1) {
|
|
const relTarget = this.cmd.gunTarget;
|
|
const relPos = this.status.gunOrient;
|
|
const diffcw = ((relPos - relTarget)+360)%360;
|
|
const diffccw = ((relTarget-relPos)+360)%360;
|
|
//console.log(relTarget, relPos, diffcw, diffccw, diffcw > diffccw);
|
|
const diff = Math.min(Math.abs((relTarget - relPos)%360), 2.5) * (diffcw > diffccw ? 1 : -1);
|
|
this.status.gunOrient = (this.status.gunOrient + diff + 360) %360;
|
|
}
|
|
}
|
|
|
|
setName(name: string) {
|
|
this.name = name;
|
|
}
|
|
|
|
setModel(model: string) {
|
|
// TODO, read from file
|
|
this.model = model;
|
|
}
|
|
setSimulationTime(time: number) {
|
|
this.status.simulationTime = time;
|
|
}
|
|
|
|
updateCmd(newCmd: RobotCmd) {
|
|
this.cmd.mergeObject(newCmd);
|
|
if (this.cmd.powerLeft > 1) {
|
|
this.cmd.powerLeft = 1;
|
|
}
|
|
if (this.cmd.powerLeft < -1) {
|
|
this.cmd.powerLeft = -1;
|
|
}
|
|
if (this.cmd.powerRight > 1) {
|
|
this.cmd.powerRight = 1;
|
|
}
|
|
if (this.cmd.powerRight < -1) {
|
|
this.cmd.powerRight = -1;
|
|
}
|
|
}
|
|
|
|
updateRadar() {
|
|
this.status.contactPoints = [];
|
|
this.world.robots.forEach((robot) => {
|
|
if (robot != this) {
|
|
// x distance to other robot
|
|
const dx = robot.status.posX - this.status.posX;
|
|
// y distance to other robot
|
|
const dy = robot.status.posY - this.status.posY;
|
|
// direction to other robot
|
|
const dir = Math.atan2(dx, -dy) / Math.PI * 180;
|
|
// distance to other robot
|
|
const dst = Math.sqrt(dx * dx + dy * dy);
|
|
// relative rotation to other robot
|
|
let rot = dir - this.status.orientation - this.status.radarPos;
|
|
// ensure -180 > rot < 180
|
|
while (rot > 180) {
|
|
rot -= 360;
|
|
}
|
|
while (rot < -180) {
|
|
rot += 360;
|
|
}
|
|
if (((this.status.radarInc && rot>=-6 && rot <=0) || (!this.status.radarInc && rot>=0 && rot <=6)) && dst < this.radarRange) {
|
|
this.status.contactPoints.push({angle: rot, dist: dst});
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
export { Robot };
|