mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 02:37:05 +02:00
Shows the chance a shot penetrates in the dynamic armor shot card, right-aligned on the shot's result line. - Penetration varies by ±5% (the same randomisation the armor highlighter shader uses), so the chance is the share of that range where the shot's final outcome is a penetration - Re-runs the existing shot calculation at different penetration values, so spaced armor, HEAT gap losses and ricochets are taken into account - For ricochets the chance is shown on the ricocheted shot's line; HE splashes don't count as penetrations - Adds the `shot_card.stats.penetration_chance` string Fixes #342
125 lines
3.0 KiB
TypeScript
125 lines
3.0 KiB
TypeScript
import {
|
|
createDefaultSkills,
|
|
ShellDefinition,
|
|
type ModelDefinition,
|
|
} from "@blitzkit/core";
|
|
import type { Vector3 } from "three";
|
|
import { Varuna } from "varuna";
|
|
import type { ArmorType } from "../components/Armor/components/SpacedArmorScene";
|
|
import type { ExternalModuleVariant } from "../components/Armor/components/SpacedArmorSceneComponent";
|
|
import type { XP_MULTIPLIERS } from "../components/Tankopedia/TechTreeSection";
|
|
import { api } from "../core/blitzkit/api";
|
|
import { App } from "./app";
|
|
import { TankopediaDisplay } from "./tankopediaPersistent/constants";
|
|
|
|
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;
|
|
splashRadius?: number;
|
|
penetrationChance?: number;
|
|
|
|
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;
|
|
};
|
|
|
|
export enum TankopediaRelativeAgainst {
|
|
Class,
|
|
Tier,
|
|
All,
|
|
}
|
|
|
|
interface Tankopedia {
|
|
disturbed: boolean;
|
|
revealed: boolean;
|
|
modelRequested: boolean;
|
|
shot?: Shot;
|
|
skills: Record<string, number>;
|
|
relativeAgainst: TankopediaRelativeAgainst;
|
|
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 }
|
|
);
|
|
xpMultiplier: (typeof XP_MULTIPLIERS)[number];
|
|
customShell?: ShellDefinition;
|
|
requestedDisplay: TankopediaDisplay;
|
|
display: TankopediaDisplay;
|
|
statSearch?: string;
|
|
}
|
|
|
|
const skillDefinitions = await api.skillDefinitions();
|
|
|
|
export const Tankopedia = new Varuna<Tankopedia, ModelDefinition>((model) => ({
|
|
disturbed: false,
|
|
revealed: false,
|
|
modelRequested: App.state.autoLoadModels,
|
|
relativeAgainst: TankopediaRelativeAgainst.Class,
|
|
editStatic: false,
|
|
skills: createDefaultSkills(skillDefinitions),
|
|
model,
|
|
xpMultiplier: 1,
|
|
requestedDisplay: TankopediaDisplay.Model,
|
|
display: TankopediaDisplay.Model,
|
|
}));
|