2024-10-18 11:20:52 -05:00
|
|
|
import {
|
|
|
|
|
createDefaultSkills,
|
2024-11-10 20:50:46 -06:00
|
|
|
ShellDefinition,
|
2024-10-18 11:20:52 -05:00
|
|
|
type ModelDefinition,
|
|
|
|
|
} from '@blitzkit/core';
|
2024-09-22 21:12:18 -05:00
|
|
|
import type { Vector3 } from 'three';
|
2024-07-09 14:43:33 -05:00
|
|
|
import { create } from 'zustand';
|
|
|
|
|
import { subscribeWithSelector } from 'zustand/middleware';
|
2024-09-22 21:12:18 -05:00
|
|
|
import type { ArmorType } from '../components/Armor/components/SpacedArmorScene';
|
2024-09-21 20:26:58 -05:00
|
|
|
import type { ExternalModuleVariant } from '../components/Armor/components/SpacedArmorSceneComponent';
|
|
|
|
|
import type { XP_MULTIPLIERS } from '../components/Tankopedia/TechTreeSection';
|
2024-11-29 13:20:27 -06:00
|
|
|
import { awaitableSkillDefinitions } from '../core/awaitables/skillDefinitions';
|
2024-09-21 20:26:58 -05:00
|
|
|
import { createContextualStore } from '../core/zustand/createContextualStore';
|
2024-12-01 20:23:23 -06:00
|
|
|
import { TankopediaDisplay } from './tankopediaPersistent/constants';
|
2024-07-09 14:43:33 -05:00
|
|
|
|
|
|
|
|
export interface ShotLayerBase {
|
|
|
|
|
index: number;
|
|
|
|
|
thickness: number;
|
|
|
|
|
point: Vector3;
|
|
|
|
|
surfaceNormal: Vector3;
|
|
|
|
|
status: 'blocked' | 'penetration' | 'ricochet';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface ShotLayerExternal extends ShotLayerBase {
|
|
|
|
|
type: ArmorType.External;
|
|
|
|
|
variant: ExternalModuleVariant;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface ShotLayerNonExternal extends ShotLayerBase {
|
|
|
|
|
type: Exclude<ArmorType, ArmorType.External>;
|
|
|
|
|
angle: number;
|
|
|
|
|
thicknessAngled: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface ShotLayerGap {
|
|
|
|
|
type: null;
|
|
|
|
|
status: 'penetration' | 'blocked';
|
|
|
|
|
distance: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export type ShotLayer = ShotLayerExternal | ShotLayerNonExternal | ShotLayerGap;
|
|
|
|
|
|
|
|
|
|
export type Shot = {
|
|
|
|
|
containsGaps: boolean;
|
|
|
|
|
damage: number;
|
2024-07-23 21:01:32 -05:00
|
|
|
splashRadius?: number;
|
2024-07-09 14:43:33 -05:00
|
|
|
|
|
|
|
|
in: {
|
|
|
|
|
surfaceNormal: Vector3;
|
|
|
|
|
status: ShotStatus;
|
|
|
|
|
layers: ShotLayer[];
|
|
|
|
|
};
|
|
|
|
|
out?: {
|
|
|
|
|
surfaceNormal: Vector3;
|
|
|
|
|
status: ShotStatus;
|
|
|
|
|
layers: ShotLayer[];
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type ShotStatus = 'penetration' | 'blocked' | 'ricochet' | 'splash';
|
|
|
|
|
|
|
|
|
|
export type ArmorPiercingLayer =
|
|
|
|
|
| {
|
|
|
|
|
distance: number;
|
|
|
|
|
nominal: number;
|
|
|
|
|
angled: number;
|
|
|
|
|
ricochet: boolean;
|
|
|
|
|
type: 'core' | 'spaced' | 'external';
|
|
|
|
|
}
|
|
|
|
|
| {
|
|
|
|
|
type: 'gap';
|
|
|
|
|
gap: number;
|
|
|
|
|
};
|
|
|
|
|
|
2024-11-12 20:58:05 -06:00
|
|
|
export enum TankopediaRelativeAgainst {
|
|
|
|
|
Class,
|
|
|
|
|
Tier,
|
|
|
|
|
All,
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-12 16:03:34 -06:00
|
|
|
export enum ShootingRangeZoom {
|
|
|
|
|
Arcade,
|
|
|
|
|
Zoom0,
|
|
|
|
|
Zoom1,
|
|
|
|
|
Zoom2,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const SHOOTING_RANGE_ZOOM_COEFFICIENTS: Record<
|
|
|
|
|
ShootingRangeZoom,
|
|
|
|
|
number
|
|
|
|
|
> = {
|
|
|
|
|
[ShootingRangeZoom.Arcade]: 1,
|
|
|
|
|
[ShootingRangeZoom.Zoom0]: 1 / 8,
|
|
|
|
|
[ShootingRangeZoom.Zoom1]: 1 / 16,
|
|
|
|
|
[ShootingRangeZoom.Zoom2]: 1 / 25,
|
|
|
|
|
};
|
|
|
|
|
|
2024-09-21 20:26:58 -05:00
|
|
|
interface TankopediaEphemeral {
|
|
|
|
|
shot?: Shot;
|
2024-12-12 16:03:34 -06:00
|
|
|
shootingRangeZoom: ShootingRangeZoom;
|
2024-09-21 20:26:58 -05:00
|
|
|
skills: Record<string, number>;
|
2024-11-12 20:58:05 -06:00
|
|
|
relativeAgainst: TankopediaRelativeAgainst;
|
2024-09-21 20:26:58 -05:00
|
|
|
controlsEnabled: boolean;
|
|
|
|
|
model: ModelDefinition;
|
|
|
|
|
editStatic: boolean;
|
|
|
|
|
highlightArmor?: {
|
|
|
|
|
editingPlate: boolean;
|
|
|
|
|
name: string;
|
|
|
|
|
point: Vector3;
|
|
|
|
|
thickness: number;
|
|
|
|
|
color: string;
|
|
|
|
|
} & (
|
|
|
|
|
| {
|
|
|
|
|
type: ArmorType.Primary | ArmorType.Spaced;
|
|
|
|
|
thicknessAngled: number;
|
|
|
|
|
angle: number;
|
|
|
|
|
}
|
|
|
|
|
| { type: ArmorType.External }
|
2024-09-14 18:32:36 -05:00
|
|
|
);
|
2024-09-21 20:26:58 -05:00
|
|
|
xpMultiplier: (typeof XP_MULTIPLIERS)[number];
|
2024-11-10 20:50:46 -06:00
|
|
|
customShell?: ShellDefinition;
|
2024-12-01 20:23:23 -06:00
|
|
|
display: TankopediaDisplay;
|
2024-09-21 20:26:58 -05:00
|
|
|
}
|
2024-09-22 21:12:18 -05:00
|
|
|
|
2024-11-29 13:20:27 -06:00
|
|
|
const skillDefinitions = await awaitableSkillDefinitions;
|
2024-10-18 11:20:52 -05:00
|
|
|
|
2024-09-22 21:12:18 -05:00
|
|
|
export const TankopediaEphemeral = createContextualStore(
|
2024-10-18 11:20:52 -05:00
|
|
|
(model: ModelDefinition) => {
|
|
|
|
|
return create<TankopediaEphemeral>()(
|
2024-09-22 21:12:18 -05:00
|
|
|
subscribeWithSelector<TankopediaEphemeral>(() => ({
|
2024-12-12 16:03:34 -06:00
|
|
|
shootingRangeZoom: ShootingRangeZoom.Arcade,
|
2024-11-12 20:58:05 -06:00
|
|
|
relativeAgainst: TankopediaRelativeAgainst.Class,
|
2024-09-22 21:12:18 -05:00
|
|
|
editStatic: false,
|
2024-10-18 11:20:52 -05:00
|
|
|
skills: createDefaultSkills(skillDefinitions),
|
2024-09-22 21:12:18 -05:00
|
|
|
model,
|
|
|
|
|
controlsEnabled: true,
|
|
|
|
|
xpMultiplier: 1,
|
2024-12-13 16:39:09 -06:00
|
|
|
display: TankopediaDisplay.Model,
|
2024-09-22 21:12:18 -05:00
|
|
|
})),
|
2024-10-18 11:20:52 -05:00
|
|
|
);
|
|
|
|
|
},
|
2024-09-22 21:12:18 -05:00
|
|
|
);
|