Spaces:
Sleeping
Sleeping
| /** | |
| * @license | |
| * SPDX-License-Identifier: Apache-2.0 | |
| */ | |
| import { Entity } from "./types/core"; | |
| import { | |
| PhysicalComponent, | |
| LocomotiveComponent, | |
| DestructibleComponent, | |
| WeaponizedComponent, | |
| SensoryComponent, | |
| ControlComponent, | |
| VisualComponent | |
| } from "./types/components"; | |
| export enum AircraftClass { | |
| Balanced = "Balanced", | |
| Turnfighter = "Turnfighter", | |
| EnergyFighter = "EnergyFighter", | |
| Attacker = "Attacker", | |
| HeavyFighter = "HeavyFighter", | |
| } | |
| export enum ControlMode { | |
| MouseAim = "Mouse Aim", | |
| MouseJoystick = "Mouse Joystick", | |
| KeyboardDirect = "Keyboard Direct", | |
| } | |
| export interface AircraftSpecs { | |
| id: string; | |
| name: string; | |
| class: AircraftClass; | |
| description: string; | |
| mass: number; // kg | |
| maxThrust: number; // N | |
| cd0: number; // CDo — zero-lift drag coefficient (dimensionless) | |
| cl0: number; // CL0 — lift at zero AoA from wing camber (dimensionless) | |
| clAlpha: number; // CLα — lift curve slope, per degree (deg⁻¹) | |
| energyRetention: number; // speed retention through maneuvers [0.85–0.99] | |
| stallSpeedKmph: number; | |
| structuralLimitSpeedKmph: number; | |
| turnBleed: number; | |
| climbRate: number; // m/s at sea level | |
| maxFuelSeconds: number; | |
| weapons: WeaponType[]; | |
| durability: number; | |
| wingArea: number; // Sw, m² | |
| aspectRatio: number; // AR | |
| oswaldEfficiency: number; // e — Oswald span efficiency | |
| aileronBoost?: number; // aileron area multiplier (default 1.0) | |
| pitchRateDegPerSec?: number; | |
| rollRateDegPerSec?: number; | |
| yawRateDegPerSec?: number; | |
| radarRange?: number; // lead-indicator lock range in world units (default 4500) | |
| color: string; | |
| secondaryColor: string; | |
| accentColor: string; | |
| } | |
| export enum WeaponType { | |
| MG_7_7 = "7.7mm MG", | |
| HMG_12_7 = "12.7mm HMG", | |
| CANNON_20 = "20mm Cannon", | |
| CANNON_30 = "30mm Cannon", | |
| ROCKET = "Rockets", | |
| BOMB = "Small Bombs", | |
| } | |
| export interface WeaponSpecs { | |
| type: WeaponType; | |
| damage: number; | |
| fireRate: number; // rounds per second | |
| muzzleVelocity: number; // m/s | |
| ammoCapacity: number; | |
| burstCount: number; // for visual tracer grouping | |
| dispersion: number; // angle variation | |
| splashRadius?: number; // meters | |
| soundType: string; | |
| } | |
| export enum AmmoBelt { | |
| Universal = "Universal", | |
| Tracer = "Tracer", | |
| ArmorPiercing = "Armor-Piercing", | |
| Incendiary = "Incendiary", | |
| Stealth = "Stealth", | |
| } | |
| export enum ModificationSlot { | |
| Engine = "Engine Tuning", | |
| Airframe = "Airframe Weight", | |
| Weapon = "Weapon Polishing", | |
| } | |
| export interface Modification { | |
| id: string; | |
| name: string; | |
| description: string; | |
| slot: ModificationSlot; | |
| effects: { | |
| maxThrust?: number; // fractional change, e.g. +0.08 | |
| mass?: number; // fractional change | |
| cd0?: number; // absolute delta to CDo | |
| rollRate?: number; | |
| durability?: number; | |
| damage?: number; // fractional change | |
| dispersion?: number; // fractional change | |
| }; | |
| } | |
| export enum MatchMode { | |
| AirSupremacy = "Air Supremacy", | |
| GroundStrike = "Ground Strike", | |
| Intercept = "Intercept", | |
| DuelArena = "Duel Arena", | |
| EndlessFront = "Endless Front", | |
| } | |
| export { KnownMaps } from "./game/content/maps/mapTypes"; | |
| export type { MapId } from "./game/content/maps/mapTypes"; | |
| export type CameraMode = "third-person" | "first-person" | "bombsight"; | |
| export interface BombSightInfo { | |
| x: number; | |
| y: number; | |
| timeToImpact: number; | |
| impactX: number; | |
| impactZ: number; | |
| valid: boolean; | |
| } | |
| export type CampaignObjectiveType = "destroy-ground" | "destroy-air"; | |
| export interface CampaignMissionDefinition { | |
| id: string; | |
| order: number; | |
| name: string; | |
| operation: string; | |
| briefing: string; | |
| mapId: string; | |
| mode: MatchMode; | |
| aircraftId: string; | |
| objectiveType: CampaignObjectiveType; | |
| objectiveLabel: string; | |
| targetCount: number; | |
| timeLimitSeconds: number; | |
| xpReward: number; | |
| startOnGround?: boolean; | |
| } | |
| export interface CampaignMissionState { | |
| missionId: string; | |
| name: string; | |
| objectiveLabel: string; | |
| progress: number; | |
| targetCount: number; | |
| completed: boolean; | |
| } | |
| // 8 Hit zones per aircraft | |
| export interface DamageModel { | |
| engine: number; // 1.0 (fully working) to 0.0 (dead/on fire) | |
| leftWing: number; // 1.0 to 0.0 | |
| rightWing: number; // 1.0 to 0.0 | |
| tail: number; // 1.0 to 0.0 | |
| cockpit: number; // 1.0 to 0.0 (pilot injured, red screen, blurry) | |
| fuelTank: number; // 1.0 to 0.0 (leaking / catching fire) | |
| fuselage: number; // 1.0 to 0.0 (center structure) | |
| hasFire: boolean; // burns engine / fuel tank over time | |
| hasOilLeak: boolean; // splatters on windshield | |
| } | |
| export interface KeyState { | |
| w: boolean; | |
| s: boolean; | |
| a: boolean; | |
| d: boolean; | |
| q: boolean; | |
| e: boolean; | |
| b: boolean; | |
| f: boolean; | |
| g: boolean; | |
| shift: boolean; | |
| control: boolean; | |
| space: boolean; | |
| r: boolean; | |
| arrowUp: boolean; | |
| arrowDown: boolean; | |
| arrowLeft: boolean; | |
| arrowRight: boolean; | |
| } | |
| export interface InputEdges { | |
| flapsPressed: boolean; | |
| gearPressed: boolean; | |
| cameraPressed: boolean; | |
| resetPressed: boolean; | |
| } | |
| export interface InputFrame { | |
| held: KeyState; | |
| edges: InputEdges; | |
| mousePos: { x: number; y: number }; | |
| mouseDelta: { x: number; y: number }; | |
| rightMouse: boolean; | |
| } | |
| export interface FlightCommand { | |
| pitch: number; // -1 nose down, +1 nose up | |
| roll: number; // -1 left, +1 right | |
| yaw: number; // -1 left, +1 right | |
| throttleDelta: number; // -1 to +1 | |
| boost: boolean; | |
| airbrake: boolean; | |
| primaryFire: boolean; | |
| secondaryFire: boolean; | |
| flaps: "up" | "combat" | "landing"; | |
| gearDeployed: boolean; | |
| } | |
| export class Pilot { | |
| public entity: Entity; | |
| public invulnerableTimer?: number; | |
| public physicsTime?: number; | |
| public physicsDebug?: { | |
| aoaDeg: number; | |
| sideslipDeg: number; | |
| mach: number; | |
| dynamicPressure: number; | |
| aeroTorqueX: number; | |
| aeroTorqueY: number; | |
| aeroTorqueZ: number; | |
| leftWingStalled: boolean; | |
| rightWingStalled: boolean; | |
| stallSeverity: number; | |
| elevatorDeflection: number; | |
| aileronDeflection: number; | |
| rudderDeflection: number; | |
| }; | |
| public netSnap?: { | |
| x: number; y: number; z: number; | |
| vx: number; vy: number; vz: number; | |
| qx: number; qy: number; qz: number; qw: number; | |
| at: number; | |
| }; | |
| constructor(data?: Partial<Pilot> | Entity) { | |
| if (data && "components" in data) { | |
| this.entity = data as Entity; | |
| return; | |
| } | |
| const pData = data as Partial<Pilot> | undefined; | |
| const entity: Entity = { | |
| id: pData?.id ?? "", | |
| components: new Map() | |
| }; | |
| this.entity = entity; | |
| const physical: PhysicalComponent = { | |
| type: "physical", | |
| x: pData?.x ?? 0, | |
| y: pData?.y ?? 0, | |
| z: pData?.z ?? 0, | |
| vx: pData?.vx ?? 0, | |
| vy: pData?.vy ?? 0, | |
| vz: pData?.vz ?? 0, | |
| qx: pData?.qx ?? 0, | |
| qy: pData?.qy ?? 0, | |
| qz: pData?.qz ?? 0, | |
| qw: pData?.qw ?? 1, | |
| avx: pData?.avx, | |
| avy: pData?.avy, | |
| avz: pData?.avz | |
| }; | |
| entity.components.set("physical", physical); | |
| const locomotive: LocomotiveComponent = { | |
| type: "locomotive", | |
| movementType: "aerodynamic", | |
| mass: pData?.specs?.mass, | |
| maxThrust: pData?.specs?.maxThrust, | |
| throttle: pData?.throttle ?? 0.8, | |
| engineTemperature: pData?.engineTemperature ?? 75, | |
| isStalling: pData?.isStalling, | |
| stallSeverity: pData?.stallSeverity, | |
| flaps: pData?.flaps, | |
| gearDeployed: pData?.gearDeployed, | |
| airbrakeDeployed: pData?.airbrakeDeployed | |
| }; | |
| entity.components.set("locomotive", locomotive); | |
| const destructible: DestructibleComponent = { | |
| type: "destructible", | |
| hp: 100, | |
| maxHp: 100, | |
| isDead: (pData?.damage?.fuselage ?? 1) <= 0.05, | |
| damageModel: pData?.damage | |
| }; | |
| entity.components.set("destructible", destructible); | |
| const weaponized: WeaponizedComponent = { | |
| type: "weaponized", | |
| weapons: pData?.specs?.weapons ?? [], | |
| ammo: (pData?.ammo ?? {}) as Record<WeaponType, number>, | |
| ammoBelt: pData?.ammoBelt ?? AmmoBelt.Universal, | |
| modifications: pData?.modifications ?? [], | |
| cooldowns: ((pData as any)?.weaponCooldowns ?? {}) as Record<WeaponType, number> | |
| }; | |
| entity.components.set("weaponized", weaponized); | |
| const sensory: SensoryComponent = { | |
| type: "sensory", | |
| radarRange: pData?.specs?.radarRange ?? 4500, | |
| lockedTargetId: pData?.aiState?.targetId ?? null | |
| }; | |
| entity.components.set("sensory", sensory); | |
| const control: ControlComponent = { | |
| type: "control", | |
| controllerType: pData?.isBot ? "bot-air" : "human", | |
| smoothedPitch: pData?.smoothedPitch, | |
| smoothedRoll: pData?.smoothedRoll, | |
| smoothedYaw: pData?.smoothedYaw, | |
| elevatorDeflection: pData?.elevatorDeflection, | |
| aileronDeflection: pData?.aileronDeflection, | |
| rudderDeflection: pData?.rudderDeflection, | |
| pitchIntent: pData?.pitchIntent, | |
| rollIntent: pData?.rollIntent, | |
| yawIntent: pData?.yawIntent, | |
| lastCommand: pData?.lastCommand, | |
| controlMode: pData?.controlMode, | |
| invertMouseY: pData?.invertMouseY, | |
| invertMouseX: pData?.invertMouseX, | |
| aiState: pData?.aiState | |
| }; | |
| entity.components.set("control", control); | |
| const visual: VisualComponent = { | |
| type: "visual", | |
| meshType: "voxel", | |
| assetId: pData?.aircraftId ?? "", | |
| color: pData?.specs?.color ?? "", | |
| secondaryColor: pData?.specs?.secondaryColor, | |
| accentColor: pData?.specs?.accentColor | |
| }; | |
| entity.components.set("visual", visual); | |
| (entity as any).name = pData?.name ?? ""; | |
| (entity as any).team = pData?.team ?? 1; | |
| (entity as any).score = pData?.score ?? 0; | |
| (entity as any).kills = pData?.kills ?? 0; | |
| (entity as any).deaths = pData?.deaths ?? 0; | |
| (entity as any).xpEarned = pData?.xpEarned ?? 0; | |
| (entity as any)._specs = pData?.specs ?? null; | |
| this.invulnerableTimer = pData?.invulnerableTimer ?? 0; | |
| this.physicsTime = pData?.physicsTime; | |
| this.physicsDebug = pData?.physicsDebug; | |
| this.netSnap = pData?.netSnap; | |
| } | |
| get id(): string { return this.entity.id; } | |
| set id(v: string) { this.entity.id = v; } | |
| get name(): string { return (this.entity as any).name; } | |
| set name(v: string) { (this.entity as any).name = v; } | |
| get isBot(): boolean { | |
| const c = this.entity.components.get("control") as ControlComponent; | |
| return c.controllerType !== "human"; | |
| } | |
| set isBot(v: boolean) { | |
| const c = this.entity.components.get("control") as ControlComponent; | |
| c.controllerType = v ? "bot-air" : "human"; | |
| } | |
| get team(): 1 | 2 { return (this.entity as any).team; } | |
| set team(v: 1 | 2) { (this.entity as any).team = v; } | |
| get aircraftId(): string { | |
| const v = this.entity.components.get("visual") as VisualComponent; | |
| return v.assetId; | |
| } | |
| set aircraftId(v: string) { | |
| const vis = this.entity.components.get("visual") as VisualComponent; | |
| vis.assetId = v; | |
| } | |
| get specs(): AircraftSpecs { | |
| return (this.entity as any)._specs as AircraftSpecs; | |
| } | |
| set specs(v: AircraftSpecs) { | |
| (this.entity as any)._specs = v; | |
| const vis = this.entity.components.get("visual") as VisualComponent; | |
| const loc = this.entity.components.get("locomotive") as LocomotiveComponent; | |
| const sen = this.entity.components.get("sensory") as SensoryComponent; | |
| const wep = this.entity.components.get("weaponized") as WeaponizedComponent; | |
| vis.assetId = v.id; | |
| vis.color = v.color; | |
| vis.secondaryColor = v.secondaryColor; | |
| vis.accentColor = v.accentColor; | |
| loc.mass = v.mass; | |
| loc.maxThrust = v.maxThrust; | |
| sen.radarRange = v.radarRange ?? 4500; | |
| wep.weapons = v.weapons; | |
| } | |
| get x(): number { return (this.entity.components.get("physical") as PhysicalComponent).x; } | |
| set x(v: number) { (this.entity.components.get("physical") as PhysicalComponent).x = v; } | |
| get y(): number { return (this.entity.components.get("physical") as PhysicalComponent).y; } | |
| set y(v: number) { (this.entity.components.get("physical") as PhysicalComponent).y = v; } | |
| get z(): number { return (this.entity.components.get("physical") as PhysicalComponent).z; } | |
| set z(v: number) { (this.entity.components.get("physical") as PhysicalComponent).z = v; } | |
| get vx(): number { return (this.entity.components.get("physical") as PhysicalComponent).vx; } | |
| set vx(v: number) { (this.entity.components.get("physical") as PhysicalComponent).vx = v; } | |
| get vy(): number { return (this.entity.components.get("physical") as PhysicalComponent).vy; } | |
| set vy(v: number) { (this.entity.components.get("physical") as PhysicalComponent).vy = v; } | |
| get vz(): number { return (this.entity.components.get("physical") as PhysicalComponent).vz; } | |
| set vz(v: number) { (this.entity.components.get("physical") as PhysicalComponent).vz = v; } | |
| get qx(): number { return (this.entity.components.get("physical") as PhysicalComponent).qx; } | |
| set qx(v: number) { (this.entity.components.get("physical") as PhysicalComponent).qx = v; } | |
| get qy(): number { return (this.entity.components.get("physical") as PhysicalComponent).qy; } | |
| set qy(v: number) { (this.entity.components.get("physical") as PhysicalComponent).qy = v; } | |
| get qz(): number { return (this.entity.components.get("physical") as PhysicalComponent).qz; } | |
| set qz(v: number) { (this.entity.components.get("physical") as PhysicalComponent).qz = v; } | |
| get qw(): number { return (this.entity.components.get("physical") as PhysicalComponent).qw; } | |
| set qw(v: number) { (this.entity.components.get("physical") as PhysicalComponent).qw = v; } | |
| get avx(): number | undefined { return (this.entity.components.get("physical") as PhysicalComponent).avx; } | |
| set avx(v: number | undefined) { (this.entity.components.get("physical") as PhysicalComponent).avx = v; } | |
| get avy(): number | undefined { return (this.entity.components.get("physical") as PhysicalComponent).avy; } | |
| set avy(v: number | undefined) { (this.entity.components.get("physical") as PhysicalComponent).avy = v; } | |
| get avz(): number | undefined { return (this.entity.components.get("physical") as PhysicalComponent).avz; } | |
| set avz(v: number | undefined) { (this.entity.components.get("physical") as PhysicalComponent).avz = v; } | |
| get throttle(): number { return (this.entity.components.get("locomotive") as LocomotiveComponent).throttle; } | |
| set throttle(v: number) { (this.entity.components.get("locomotive") as LocomotiveComponent).throttle = v; } | |
| get engineTemperature(): number { return (this.entity.components.get("locomotive") as LocomotiveComponent).engineTemperature ?? 75; } | |
| set engineTemperature(v: number) { (this.entity.components.get("locomotive") as LocomotiveComponent).engineTemperature = v; } | |
| get isStalling(): boolean | undefined { return (this.entity.components.get("locomotive") as LocomotiveComponent).isStalling; } | |
| set isStalling(v: boolean | undefined) { (this.entity.components.get("locomotive") as LocomotiveComponent).isStalling = v; } | |
| get stallSeverity(): number | undefined { return (this.entity.components.get("locomotive") as LocomotiveComponent).stallSeverity; } | |
| set stallSeverity(v: number | undefined) { (this.entity.components.get("locomotive") as LocomotiveComponent).stallSeverity = v; } | |
| get flaps(): "up" | "combat" | "landing" | undefined { return (this.entity.components.get("locomotive") as LocomotiveComponent).flaps; } | |
| set flaps(v: "up" | "combat" | "landing" | undefined) { (this.entity.components.get("locomotive") as LocomotiveComponent).flaps = v; } | |
| get gearDeployed(): boolean | undefined { return (this.entity.components.get("locomotive") as LocomotiveComponent).gearDeployed; } | |
| set gearDeployed(v: boolean | undefined) { (this.entity.components.get("locomotive") as LocomotiveComponent).gearDeployed = v; } | |
| get airbrakeDeployed(): boolean | undefined { return (this.entity.components.get("locomotive") as LocomotiveComponent).airbrakeDeployed; } | |
| set airbrakeDeployed(v: boolean | undefined) { (this.entity.components.get("locomotive") as LocomotiveComponent).airbrakeDeployed = v; } | |
| get damage(): DamageModel { return (this.entity.components.get("destructible") as DestructibleComponent).damageModel!; } | |
| set damage(v: DamageModel) { | |
| const d = this.entity.components.get("destructible") as DestructibleComponent; | |
| d.damageModel = v; | |
| d.isDead = v.fuselage <= 0.05; | |
| } | |
| get ammo(): Record<WeaponType, number> { return (this.entity.components.get("weaponized") as WeaponizedComponent).ammo; } | |
| set ammo(v: Record<WeaponType, number>) { (this.entity.components.get("weaponized") as WeaponizedComponent).ammo = v; } | |
| get ammoBelt(): AmmoBelt { return (this.entity.components.get("weaponized") as WeaponizedComponent).ammoBelt; } | |
| set ammoBelt(v: AmmoBelt) { (this.entity.components.get("weaponized") as WeaponizedComponent).ammoBelt = v; } | |
| get modifications(): string[] { return (this.entity.components.get("weaponized") as WeaponizedComponent).modifications; } | |
| set modifications(v: string[]) { (this.entity.components.get("weaponized") as WeaponizedComponent).modifications = v; } | |
| get weaponCooldowns(): Partial<Record<WeaponType, number>> { return (this.entity.components.get("weaponized") as WeaponizedComponent).cooldowns; } | |
| set weaponCooldowns(v: Partial<Record<WeaponType, number>>) { (this.entity.components.get("weaponized") as WeaponizedComponent).cooldowns = v as any; } | |
| get smoothedPitch(): number | undefined { return (this.entity.components.get("control") as ControlComponent).smoothedPitch; } | |
| set smoothedPitch(v: number | undefined) { (this.entity.components.get("control") as ControlComponent).smoothedPitch = v; } | |
| get smoothedRoll(): number | undefined { return (this.entity.components.get("control") as ControlComponent).smoothedRoll; } | |
| set smoothedRoll(v: number | undefined) { (this.entity.components.get("control") as ControlComponent).smoothedRoll = v; } | |
| get smoothedYaw(): number | undefined { return (this.entity.components.get("control") as ControlComponent).smoothedYaw; } | |
| set smoothedYaw(v: number | undefined) { (this.entity.components.get("control") as ControlComponent).smoothedYaw = v; } | |
| get elevatorDeflection(): number | undefined { return (this.entity.components.get("control") as ControlComponent).elevatorDeflection; } | |
| set elevatorDeflection(v: number | undefined) { (this.entity.components.get("control") as ControlComponent).elevatorDeflection = v; } | |
| get aileronDeflection(): number | undefined { return (this.entity.components.get("control") as ControlComponent).aileronDeflection; } | |
| set aileronDeflection(v: number | undefined) { (this.entity.components.get("control") as ControlComponent).aileronDeflection = v; } | |
| get rudderDeflection(): number | undefined { return (this.entity.components.get("control") as ControlComponent).rudderDeflection; } | |
| set rudderDeflection(v: number | undefined) { (this.entity.components.get("control") as ControlComponent).rudderDeflection = v; } | |
| get pitchIntent(): number | undefined { return (this.entity.components.get("control") as ControlComponent).pitchIntent; } | |
| set pitchIntent(v: number | undefined) { (this.entity.components.get("control") as ControlComponent).pitchIntent = v; } | |
| get rollIntent(): number | undefined { return (this.entity.components.get("control") as ControlComponent).rollIntent; } | |
| set rollIntent(v: number | undefined) { (this.entity.components.get("control") as ControlComponent).rollIntent = v; } | |
| get yawIntent(): number | undefined { return (this.entity.components.get("control") as ControlComponent).yawIntent; } | |
| set yawIntent(v: number | undefined) { (this.entity.components.get("control") as ControlComponent).yawIntent = v; } | |
| get lastCommand(): FlightCommand | undefined { return (this.entity.components.get("control") as ControlComponent).lastCommand; } | |
| set lastCommand(v: FlightCommand | undefined) { (this.entity.components.get("control") as ControlComponent).lastCommand = v; } | |
| get controlMode(): ControlMode | undefined { return (this.entity.components.get("control") as ControlComponent).controlMode; } | |
| set controlMode(v: ControlMode | undefined) { (this.entity.components.get("control") as ControlComponent).controlMode = v; } | |
| get invertMouseY(): boolean | undefined { return (this.entity.components.get("control") as ControlComponent).invertMouseY; } | |
| set invertMouseY(v: boolean | undefined) { (this.entity.components.get("control") as ControlComponent).invertMouseY = v; } | |
| get invertMouseX(): boolean | undefined { return (this.entity.components.get("control") as ControlComponent).invertMouseX; } | |
| set invertMouseX(v: boolean | undefined) { (this.entity.components.get("control") as ControlComponent).invertMouseX = v; } | |
| get aiState(): any { return (this.entity.components.get("control") as ControlComponent).aiState; } | |
| set aiState(v: any) { (this.entity.components.get("control") as ControlComponent).aiState = v; } | |
| get score(): number { return (this.entity as any).score; } | |
| set score(v: number) { (this.entity as any).score = v; } | |
| get kills(): number { return (this.entity as any).kills; } | |
| set kills(v: number) { (this.entity as any).kills = v; } | |
| get deaths(): number { return (this.entity as any).deaths; } | |
| set deaths(v: number) { (this.entity as any).deaths = v; } | |
| get xpEarned(): number { return (this.entity as any).xpEarned; } | |
| set xpEarned(v: number) { (this.entity as any).xpEarned = v; } | |
| } | |
| export interface Projectile { | |
| id: string; | |
| ownerId: string; | |
| ownerTeam: 1 | 2; | |
| type: WeaponType; | |
| belt: AmmoBelt; | |
| x: number; | |
| y: number; | |
| z: number; | |
| vx: number; | |
| vy: number; | |
| vz: number; | |
| life: number; // remaining frames/seconds | |
| isRocket: boolean; | |
| } | |
| export class GroundTarget { | |
| public entity: Entity; | |
| constructor(data?: Partial<GroundTarget> | Entity) { | |
| if (data && "components" in data) { | |
| this.entity = data as Entity; | |
| return; | |
| } | |
| const tData = data as Partial<GroundTarget> | undefined; | |
| const entity: Entity = { | |
| id: tData?.id ?? "", | |
| components: new Map() | |
| }; | |
| this.entity = entity; | |
| const physical: PhysicalComponent = { | |
| type: "physical", | |
| x: tData?.x ?? 0, | |
| y: tData?.y ?? 0, | |
| z: tData?.z ?? 0, | |
| vx: 0, vy: 0, vz: 0, | |
| qx: 0, qy: 0, qz: 0, qw: 1 | |
| }; | |
| entity.components.set("physical", physical); | |
| const destructible: DestructibleComponent = { | |
| type: "destructible", | |
| hp: tData?.hp ?? 100, | |
| maxHp: tData?.maxHp ?? 100, | |
| isDead: tData?.isDead ?? false, | |
| fireCooldown: tData?.fireCooldown | |
| }; | |
| entity.components.set("destructible", destructible); | |
| const visual: VisualComponent = { | |
| type: "visual", | |
| meshType: "primitive", | |
| assetId: tData?.type ?? "", | |
| color: "" | |
| }; | |
| entity.components.set("visual", visual); | |
| (entity as any).name = tData?.name ?? ""; | |
| (entity as any).team = tData?.team ?? 1; | |
| } | |
| get id(): string { return this.entity.id; } | |
| set id(v: string) { this.entity.id = v; } | |
| get name(): string { return (this.entity as any).name; } | |
| set name(v: string) { (this.entity as any).name = v; } | |
| get team(): 1 | 2 { return (this.entity as any).team; } | |
| set team(v: 1 | 2) { (this.entity as any).team = v; } | |
| get type(): "convoy" | "radar" | "anti-air" | "bunker" | "carrier" { | |
| return (this.entity.components.get("visual") as VisualComponent).assetId as any; | |
| } | |
| set type(v: "convoy" | "radar" | "anti-air" | "bunker" | "carrier") { | |
| (this.entity.components.get("visual") as VisualComponent).assetId = v; | |
| } | |
| get x(): number { return (this.entity.components.get("physical") as PhysicalComponent).x; } | |
| set x(v: number) { (this.entity.components.get("physical") as PhysicalComponent).x = v; } | |
| get y(): number { return (this.entity.components.get("physical") as PhysicalComponent).y; } | |
| set y(v: number) { (this.entity.components.get("physical") as PhysicalComponent).y = v; } | |
| get z(): number { return (this.entity.components.get("physical") as PhysicalComponent).z; } | |
| set z(v: number) { (this.entity.components.get("physical") as PhysicalComponent).z = v; } | |
| get hp(): number { return (this.entity.components.get("destructible") as DestructibleComponent).hp; } | |
| set hp(v: number) { (this.entity.components.get("destructible") as DestructibleComponent).hp = v; } | |
| get maxHp(): number { return (this.entity.components.get("destructible") as DestructibleComponent).maxHp; } | |
| set maxHp(v: number) { (this.entity.components.get("destructible") as DestructibleComponent).maxHp = v; } | |
| get isDead(): boolean { return (this.entity.components.get("destructible") as DestructibleComponent).isDead; } | |
| set isDead(v: boolean) { (this.entity.components.get("destructible") as DestructibleComponent).isDead = v; } | |
| get fireCooldown(): number | undefined { return (this.entity.components.get("destructible") as DestructibleComponent).fireCooldown; } | |
| set fireCooldown(v: number | undefined) { (this.entity.components.get("destructible") as DestructibleComponent).fireCooldown = v; } | |
| } | |
| export interface SkyZone { | |
| id: string; | |
| name: string; | |
| x: number; | |
| y: number; | |
| z: number; | |
| radius: number; | |
| owningTeam: 0 | 1 | 2; // 0 = neutral, 1 = team 1, 2 = team 2 | |
| captureProgress: number; // -100 to 100 | |
| } | |
| export interface BomberWave { | |
| id: string; | |
| team: 1 | 2; | |
| x: number; | |
| y: number; | |
| z: number; | |
| hp: number; | |
| maxHp: number; | |
| isDead: boolean; | |
| targetX: number; | |
| targetZ: number; | |
| } | |
| export interface UserProgression { | |
| totalXp: number; | |
| planeXp: Record<string, number>; // aircraftSpecs.id -> xp | |
| unlockedPlanes: string[]; // aircraftSpecs.id | |
| equippedMods: Record<string, string[]>; // aircraftSpecs.id -> modId[] | |
| selectedPlaneId: string; | |
| selectedBelt: AmmoBelt; | |
| nickname?: string; | |
| isLoggedIn?: boolean; | |
| rankCode?: string; | |
| invertMouseY?: boolean; | |
| invertMouseX?: boolean; | |
| controlMode?: ControlMode; | |
| completedCampaignMissions?: string[]; | |
| stats: { | |
| battlesPlayed: number; | |
| kills: number; | |
| deaths: number; | |
| groundTargetsDestroyed: number; | |
| victories: number; | |
| }; | |
| customizations: { | |
| skin: string; | |
| tracerColor: string; | |
| noseArt: string; | |
| }; | |
| } | |
| export interface KillEvent { | |
| id: string; | |
| killerName: string; | |
| killerTeam: 1 | 2; | |
| victimName: string; | |
| victimTeam: 1 | 2; | |
| method: string; | |
| timestamp: number; | |
| } | |
| export interface LeadIndicatorInfo { | |
| x: number; // target screen X % (0-100) | |
| y: number; // target screen Y % (0-100) | |
| sX: number; // lead dot screen X % (0-100) | |
| sY: number; // lead dot screen Y % (0-100) | |
| name: string; // pilot name | |
| distance: number; // distance in meters | |
| isBot: boolean; | |
| } | |