131 lines
4.3 KiB
TypeScript
131 lines
4.3 KiB
TypeScript
import { vec2 } from 'gl-matrix';
|
|
import {
|
|
PlayerInformation,
|
|
CharacterTeam,
|
|
settings,
|
|
Circle,
|
|
Random,
|
|
MoveActionCommand,
|
|
} from 'shared';
|
|
import { PhysicalContainer } from '../physics/containers/physical-container';
|
|
import { PlayerContainer } from './player-container';
|
|
import { PlayerBase } from './player-base';
|
|
import { getBoundingBoxOfCircle } from '../physics/functions/get-bounding-box-of-circle';
|
|
import { PlayerCharacterPhysical } from '../objects/player-character-physical';
|
|
import { PlanetPhysical } from '../objects/planet-physical';
|
|
|
|
export class NPC extends PlayerBase {
|
|
private moveTarget: vec2 = vec2.create();
|
|
private planet?: PlanetPhysical;
|
|
private timeSinceLastFindTarget = 10000;
|
|
private timeSinceLastFindShootTarget = 10000;
|
|
|
|
constructor(
|
|
playerInfo: PlayerInformation,
|
|
playerContainer: PlayerContainer,
|
|
objectContainer: PhysicalContainer,
|
|
team: CharacterTeam,
|
|
) {
|
|
super(playerInfo, playerContainer, objectContainer, team);
|
|
this.createCharacter();
|
|
this.step(0);
|
|
}
|
|
|
|
private findTarget() {
|
|
const observableArea = getBoundingBoxOfCircle(new Circle(this.center, 100));
|
|
const nearObjects = this.objectContainer.findIntersecting(observableArea);
|
|
|
|
const enemies = nearObjects.filter(
|
|
(o) =>
|
|
o.gameObject instanceof PlayerCharacterPhysical &&
|
|
o.gameObject.team !== this.team,
|
|
);
|
|
const allies = nearObjects.filter(
|
|
(o) =>
|
|
o.gameObject instanceof PlayerCharacterPhysical &&
|
|
o.gameObject.team === this.team,
|
|
);
|
|
const notControlledPlanets = nearObjects.filter(
|
|
(o) => o.gameObject instanceof PlanetPhysical && o.gameObject.team !== this.team,
|
|
);
|
|
|
|
if (enemies.length > allies.length) {
|
|
const enemiesCenter = enemies.reduce(
|
|
(sum, e) => vec2.add(sum, sum, (e.gameObject as PlayerCharacterPhysical).center),
|
|
vec2.create(),
|
|
);
|
|
vec2.scale(enemiesCenter, enemiesCenter, 1 / enemies.length);
|
|
const enemiesDelta = vec2.subtract(enemiesCenter, this.center, enemiesCenter);
|
|
if (vec2.length(enemiesDelta) > 0) {
|
|
vec2.scale(enemiesDelta, enemiesDelta, 200 / vec2.length(enemiesDelta));
|
|
}
|
|
vec2.add(this.moveTarget, this.center, enemiesDelta);
|
|
} else if (!this.planet) {
|
|
if (notControlledPlanets.length > 0) {
|
|
this.planet = notControlledPlanets[0] as PlanetPhysical;
|
|
this.moveTarget = this.planet.center;
|
|
} else {
|
|
this.moveTarget = vec2.fromValues(
|
|
Random.getRandomInRange(-5000, 5000),
|
|
Random.getRandomInRange(-5000, 5000),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
private findShootTarget(): vec2 | undefined {
|
|
const observableArea = getBoundingBoxOfCircle(new Circle(this.center, 1000));
|
|
const nearObjects = this.objectContainer.findIntersecting(observableArea);
|
|
|
|
const enemies = nearObjects.filter(
|
|
(o) =>
|
|
o.gameObject instanceof PlayerCharacterPhysical &&
|
|
o.gameObject.team !== this.team,
|
|
);
|
|
if (enemies.length > 0) {
|
|
return (enemies[0].gameObject as PlayerCharacterPhysical).center;
|
|
}
|
|
}
|
|
|
|
private timeUntilRespawn = 0;
|
|
public step(deltaTimeInSeconds: number) {
|
|
if (this.character) {
|
|
this.center = this.character?.center;
|
|
|
|
if (!this.character.isAlive) {
|
|
this.sumDeaths++;
|
|
this.sumKills = this.character.killCount;
|
|
|
|
this.character = null;
|
|
this.timeUntilRespawn = settings.playerDiedTimeout;
|
|
}
|
|
} else {
|
|
if ((this.timeUntilRespawn -= deltaTimeInSeconds) < 0) {
|
|
this.createCharacter();
|
|
this.center = this.character!.center;
|
|
}
|
|
}
|
|
|
|
if (this.planet && Math.abs(this.planet.ownership - 0.5) > 0.3) {
|
|
this.planet = undefined;
|
|
this.findTarget();
|
|
} else {
|
|
if ((this.timeSinceLastFindTarget += deltaTimeInSeconds) > 1) {
|
|
this.timeSinceLastFindTarget = 0;
|
|
this.findTarget();
|
|
}
|
|
}
|
|
|
|
if ((this.timeSinceLastFindShootTarget += deltaTimeInSeconds) > 0.5) {
|
|
const shootTarget = this.findShootTarget();
|
|
if (shootTarget) {
|
|
this.character?.shootTowards(shootTarget);
|
|
}
|
|
this.timeSinceLastFindShootTarget = 0;
|
|
}
|
|
|
|
const targetDelta = vec2.subtract(vec2.create(), this.moveTarget, this.center);
|
|
vec2.normalize(targetDelta, targetDelta);
|
|
this.character?.handleMovementAction(new MoveActionCommand(targetDelta, false));
|
|
}
|
|
}
|