airframe.io / src /game /multiplayer /BotSystem.ts
LATTICEWORKS's picture
feat: integrate Google Earth 3D Tiles and optimize engine hooks
565f6e7
Raw
History Blame Contribute Delete
3.16 kB
import { Pilot } from "../../types";
import { DEFAULT_AIRCRAFT } from "../aircraftData";
import { generateId } from "../math";
import { NetworkPlayer } from "./MatchState";
export const BOT_NAMES = [
"Striker", "Interceptor", "Spectre", "Phoenix", "Phantom",
"Grizzly", "Falcon", "Viper", "Cobra", "Reaper",
"Warlord", "Tornado", "Spitfire", "Mustang", "Zero"
];
export class BotSystem {
public spawnBot(
team: number,
aircraftId: string,
pilots: Map<string, Pilot>,
statePlayers: any,
addPhysicsBody: (id: string, pilot: Pilot) => void,
getSpawnYaw: (t: number) => number
) {
const id = "bot_" + generateId();
const name = BOT_NAMES[Math.floor(Math.random() * BOT_NAMES.length)];
// Bots spawn high up, randomly distributed
const startX = (Math.random() - 0.5) * 2000;
const startZ = team === 1 ? -3000 + Math.random() * 500 : 3000 - Math.random() * 500;
const startY = 600 + Math.random() * 400;
const pilot = new Pilot({
id,
aircraftId,
x: startX,
y: startY,
z: startZ,
qx: 0,
qy: Math.sin(getSpawnYaw(team) / 2),
qz: 0,
qw: Math.cos(getSpawnYaw(team) / 2),
vx: 0,
vy: 0,
vz: 100, // Forward speed
damage: {
engine: 1, leftWing: 1, rightWing: 1, tail: 1,
cockpit: 1, fuelTank: 1, fuselage: 1, hasFire: false, hasOilLeak: false
},
specs: DEFAULT_AIRCRAFT.find(a => a.id === aircraftId) || DEFAULT_AIRCRAFT[0]
});
pilot.team = team as 1 | 2;
pilot.isBot = true;
pilot.name = name;
// ctrl.throttleDelta is no longer used here
pilot.throttle = 1.0;
pilots.set(id, pilot);
const netPlayer = new NetworkPlayer();
netPlayer.id = id;
netPlayer.name = name;
netPlayer.team = team;
netPlayer.aircraftId = aircraftId;
netPlayer.skin = "bot-camo";
netPlayer.isBot = true;
statePlayers.set(id, netPlayer);
addPhysicsBody(id, pilot);
}
public fillWithBots(
targetCount: number,
pilots: Map<string, Pilot>,
statePlayers: any,
addPhysicsBody: (id: string, pilot: Pilot) => void,
getSpawnYaw: (t: number) => number
) {
let team1 = 0;
let team2 = 0;
for (const p of pilots.values()) {
if (p.team === 1) team1++;
if (p.team === 2) team2++;
}
while (team1 + team2 < targetCount) {
const team = team1 <= team2 ? 1 : 2;
const aircraft = DEFAULT_AIRCRAFT[Math.floor(Math.random() * DEFAULT_AIRCRAFT.length)];
this.spawnBot(team, aircraft.id, pilots, statePlayers, addPhysicsBody, getSpawnYaw);
if (team === 1) team1++;
else team2++;
}
}
public evictBotsIfFull(maxClients: number, currentHumans: number, pilots: Map<string, Pilot>, cleanupPlayer: (id: string) => void) {
let totalPlayers = pilots.size;
// If joining would exceed max, evict a bot
while (totalPlayers >= maxClients) {
const bots = Array.from(pilots.values()).filter(p => p.isBot);
if (bots.length === 0) break; // no bots to evict
const toEvict = bots[0];
cleanupPlayer(toEvict.id);
totalPlayers--;
}
}
}