File size: 3,163 Bytes
565f6e7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
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--;
    }
  }
}