File size: 3,626 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
108
109
110
import { Pilot, GroundTarget, SkyZone } from "../../types";
import { FlightPhysicsEngine } from "../flightModel";
import { destructible, control } from "../../types/components";
import { BotAISystem } from "../botAISystem";

import { FlightCommand } from "../../types";
import { MAP_REGISTRY } from "../content/maps/registry";

const FIXED_DT = 1 / 60;

export class SimulationSystem {
  public updateSimulation(
    accumulator: number,
    deltaMs: number,
    matchEnded: boolean,
    tickCallback: () => void
  ): number {
    if (matchEnded) return accumulator;

    let newAccumulator = accumulator + (deltaMs / 1000);
    if (newAccumulator > 0.1) newAccumulator = 0.1; // clamp death spiral

    while (newAccumulator >= FIXED_DT) {
      tickCallback();
      newAccumulator -= FIXED_DT;
    }

    return newAccumulator;
  }

  public tickPlayers(
    pilots: Map<string, Pilot>,
    statePlayers: any,
    getNextInput: (id: string, neutral: FlightCommand) => { command: FlightCommand, seq?: number },
    neutralCommand: () => FlightCommand,
    tickCooldowns: (p: Pilot, dt: number) => void,
    updateDeadPilot: (p: Pilot, dt: number) => void,
    enforceMapBoundary: (p: Pilot, dt: number) => void,
    handleWeaponFiring: (p: Pilot, pF: boolean, sF: boolean, dt: number, seq?: number) => void,
    syncRapierBody: (id: string, p: Pilot) => void,
    recordHistory: (id: string, p: Pilot) => void,
    mapId: string
  ) {
    for (const [sessionId, pilot] of pilots.entries()) {
      if (pilot.isBot) continue;

      const { command, seq } = getNextInput(sessionId, neutralCommand());

      const schemaPlayer = statePlayers.get(sessionId);
      if (schemaPlayer && seq !== undefined) {
        schemaPlayer.lastProcessedSeq = seq;
      }

      if (destructible(pilot.entity).damageModel!.fuselage <= 0) {
        updateDeadPilot(pilot, FIXED_DT);
      } else {
        tickCooldowns(pilot, FIXED_DT);
        FlightPhysicsEngine.update(pilot, command, FIXED_DT, mapId);
        enforceMapBoundary(pilot, FIXED_DT);
        handleWeaponFiring(pilot, command.primaryFire, command.secondaryFire, FIXED_DT, seq);
      }

      syncRapierBody(sessionId, pilot);
      recordHistory(sessionId, pilot);
    }
  }

  public tickBots(
    pilots: Map<string, Pilot>,
    groundTargets: GroundTarget[],
    skyZones: SkyZone[],
    tickCooldowns: (p: Pilot, dt: number) => void,
    updateDeadPilot: (p: Pilot, dt: number) => void,
    enforceMapBoundary: (p: Pilot, dt: number) => void,
    handleWeaponFiring: (p: Pilot, pF: boolean, sF: boolean, dt: number, seq?: number) => void,
    syncRapierBody: (id: string, p: Pilot) => void,
    recordHistory: (id: string, p: Pilot) => void,
    mapId: string,
    neutralCommand: () => FlightCommand
  ) {
    for (const [botId, pilot] of pilots.entries()) {
      if (!pilot.isBot) continue;

      if (destructible(pilot.entity).damageModel!.fuselage <= 0) {
        updateDeadPilot(pilot, FIXED_DT);
      } else {
        tickCooldowns(pilot, FIXED_DT);
        
        BotAISystem.runAIConsensus(
          pilot,
          FIXED_DT,
          Array.from(pilots.values()),
          groundTargets,
          skyZones,
          (p, prim, sec, d) => handleWeaponFiring(p, prim, sec, d),
          MAP_REGISTRY[mapId]?.world.radius ?? 6000
        );

        const botCtrl = control(pilot.entity);
        const cmd = botCtrl.lastCommand || neutralCommand();
        FlightPhysicsEngine.update(pilot, cmd, FIXED_DT, mapId);
        enforceMapBoundary(pilot, FIXED_DT);
      }

      syncRapierBody(botId, pilot);
      recordHistory(botId, pilot);
    }
  }
}