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