robotank/server-nodejs/robot.ts
Sascha Nitsch 51f0415617 initial im
2025-03-03 02:17:50 +01:00

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