mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 20:19:49 +02:00
applySpallLiner
This commit is contained in:
1 parent
1a19cff268
commit
16637dac71
10 files changed
+133
-36
No files matched your search
@@ -83,6 +83,7 @@
|
|||||||
"dynamics": "Dynamics",
|
"dynamics": "Dynamics",
|
||||||
"status": "Status",
|
"status": "Status",
|
||||||
"consumables": "Consumables",
|
"consumables": "Consumables",
|
||||||
|
"provisions": "Provisions",
|
||||||
"modules": "Modules",
|
"modules": "Modules",
|
||||||
"equipment": "Equipment",
|
"equipment": "Equipment",
|
||||||
"alternative": "Alt"
|
"alternative": "Alt"
|
||||||
|
|||||||
@@ -21,6 +21,10 @@
|
|||||||
padding: var(--space-2) var(--space-3);
|
padding: var(--space-2) var(--space-3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&[data-size="major"] {
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
&[data-list-child="true"]:not(:first-child),
|
&[data-list-child="true"]:not(:first-child),
|
||||||
*:not(:first-child) > &[data-parent-list-child="true"] {
|
*:not(:first-child) > &[data-parent-list-child="true"] {
|
||||||
border-top-left-radius: 0;
|
border-top-left-radius: 0;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export interface ButtonProps extends ComponentProps<"button"> {
|
|||||||
color?: Color;
|
color?: Color;
|
||||||
radius?: `${1 | 2 | 3}` | "max";
|
radius?: `${1 | 2 | 3}` | "max";
|
||||||
variant?: "solid" | "surface" | "soft" | "inverted" | "ghost" | "outline";
|
variant?: "solid" | "surface" | "soft" | "inverted" | "ghost" | "outline";
|
||||||
size?: "regular" | "minor";
|
size?: "regular" | "minor" | "major";
|
||||||
highContrast?: boolean; // TODO: add high contrast
|
highContrast?: boolean; // TODO: add high contrast
|
||||||
|
|
||||||
listChild?: boolean;
|
listChild?: boolean;
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
.section {
|
.section {
|
||||||
|
max-width: 21rem;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
gap: var(--space-4);
|
gap: var(--space-4);
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
@@ -41,9 +44,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.provisions {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: var(--space-3);
|
||||||
|
}
|
||||||
|
|
||||||
.consumables {
|
.consumables {
|
||||||
display: grid;
|
display: flex;
|
||||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
flex-wrap: wrap;
|
||||||
gap: var(--space-3);
|
gap: var(--space-3);
|
||||||
|
|
||||||
.consumable-wrapper {
|
.consumable-wrapper {
|
||||||
@@ -100,8 +109,6 @@
|
|||||||
aspect-ratio: 1 / 1;
|
aspect-ratio: 1 / 1;
|
||||||
padding: var(--space-3);
|
padding: var(--space-3);
|
||||||
|
|
||||||
font-size: var(--space-5);
|
|
||||||
|
|
||||||
&[data-selected="true"] {
|
&[data-selected="true"] {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { alias } from "@blitzkit/core";
|
import { alias } from "@blitzkit/core";
|
||||||
import type { Consumable, EquipmentSlot } from "@blitzkit/protos";
|
import type { Consumable, EquipmentSlot, Provision } from "@blitzkit/protos";
|
||||||
import { ClockIcon, ResetIcon, TrashIcon } from "@radix-ui/react-icons";
|
import { ClockIcon, ResetIcon, TrashIcon } from "@radix-ui/react-icons";
|
||||||
import { chunk } from "lodash-es";
|
import { chunk } from "lodash-es";
|
||||||
import { api } from "../../api/dynamic";
|
import { api } from "../../api/dynamic";
|
||||||
import { equipmentColumns } from "../../config/equipment";
|
import { equipmentColumns } from "../../config/equipment";
|
||||||
import { useConsumables } from "../../hooks/useConsumables";
|
|
||||||
import { DuelModule, useDuel } from "../../hooks/useDuel";
|
import { DuelModule, useDuel } from "../../hooks/useDuel";
|
||||||
import { useEquipmentPreset } from "../../hooks/useEquipmentPreset";
|
import { useEquipmentPreset } from "../../hooks/useEquipmentPreset";
|
||||||
|
import { useProvision } from "../../hooks/useProvision";
|
||||||
import { useStrings } from "../../hooks/useStrings";
|
import { useStrings } from "../../hooks/useStrings";
|
||||||
import { useTankCompatibility } from "../../hooks/useTankCompatibility";
|
import { useTankCompatibility } from "../../hooks/useTankCompatibility";
|
||||||
import { useUnwrapper } from "../../hooks/useUnwrapper";
|
import { useUnwrapper } from "../../hooks/useUnwrapper";
|
||||||
@@ -22,6 +22,8 @@ import { Tooltip } from "../Tooltip";
|
|||||||
import styles from "./index.module.css";
|
import styles from "./index.module.css";
|
||||||
|
|
||||||
const equipment = await api.equipment();
|
const equipment = await api.equipment();
|
||||||
|
const provisions = await api.provisions();
|
||||||
|
const consumables = await api.consumables();
|
||||||
|
|
||||||
interface TankopediaLoadoutProps {
|
interface TankopediaLoadoutProps {
|
||||||
characteristics: ComputedCharacteristics;
|
characteristics: ComputedCharacteristics;
|
||||||
@@ -35,6 +37,7 @@ export function TankopediaLoadout({ characteristics }: TankopediaLoadoutProps) {
|
|||||||
<>
|
<>
|
||||||
{/* {showModules && <Modules />} */}
|
{/* {showModules && <Modules />} */}
|
||||||
<Equipment />
|
<Equipment />
|
||||||
|
<Provisions />
|
||||||
<Consumables characteristics={characteristics} />
|
<Consumables characteristics={characteristics} />
|
||||||
<State />
|
<State />
|
||||||
<Status />
|
<Status />
|
||||||
@@ -122,13 +125,88 @@ function Status() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Provisions() {
|
||||||
|
const strings = useStrings();
|
||||||
|
const tank = useDuel("protagonist", DuelModule.Tank);
|
||||||
|
const gun = useDuel("protagonist", DuelModule.Gun);
|
||||||
|
const isCompatible = useTankCompatibility(tank, gun);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.section}>
|
||||||
|
<div className={styles.header}>
|
||||||
|
<Heading size="4">{strings.tanks.loadout.provisions}</Heading>
|
||||||
|
|
||||||
|
<IconButton
|
||||||
|
color="red"
|
||||||
|
onClick={() => {
|
||||||
|
Tankopedia.mutate((draft) => {
|
||||||
|
draft.protagonist.provisions = [];
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TrashIcon />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.provisions}>
|
||||||
|
{Object.entries(provisions.provisions).map(([id, provision]) => {
|
||||||
|
if (
|
||||||
|
provision.game_mode_exclusive ||
|
||||||
|
!isCompatible(provision.include, provision.exclude)
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProvisionEntry key={id} id={Number(id)} provision={provision} />
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProvisionEntryProps {
|
||||||
|
id: number;
|
||||||
|
provision: Provision;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ProvisionEntry({ id, provision }: ProvisionEntryProps) {
|
||||||
|
const isSelected = useProvision("protagonist", id);
|
||||||
|
const unwrap = useUnwrapper();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip tooltip={unwrap(provision.name)}>
|
||||||
|
<IconButton
|
||||||
|
size="major"
|
||||||
|
radius="1"
|
||||||
|
variant={isSelected ? "surface" : "soft"}
|
||||||
|
color={isSelected ? undefined : "gray"}
|
||||||
|
className={styles.consumable}
|
||||||
|
onClick={() => {
|
||||||
|
Tankopedia.mutate((draft) => {
|
||||||
|
const index = draft.protagonist.provisions.indexOf(id);
|
||||||
|
|
||||||
|
if (index === -1) {
|
||||||
|
draft.protagonist.provisions.push(id);
|
||||||
|
} else {
|
||||||
|
draft.protagonist.provisions.splice(index, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<img src={alias("api", `/icons/provisions/${provision.id}.webp`)} />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
interface ConsumablesProps {
|
interface ConsumablesProps {
|
||||||
characteristics: ComputedCharacteristics;
|
characteristics: ComputedCharacteristics;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Consumables({ characteristics }: ConsumablesProps) {
|
function Consumables({ characteristics }: ConsumablesProps) {
|
||||||
const strings = useStrings();
|
const strings = useStrings();
|
||||||
const consumables = useConsumables();
|
|
||||||
const tank = useDuel("protagonist", DuelModule.Tank);
|
const tank = useDuel("protagonist", DuelModule.Tank);
|
||||||
const gun = useDuel("protagonist", DuelModule.Gun);
|
const gun = useDuel("protagonist", DuelModule.Gun);
|
||||||
const isCompatible = useTankCompatibility(tank, gun);
|
const isCompatible = useTankCompatibility(tank, gun);
|
||||||
@@ -152,7 +230,10 @@ function Consumables({ characteristics }: ConsumablesProps) {
|
|||||||
|
|
||||||
<div className={styles.consumables}>
|
<div className={styles.consumables}>
|
||||||
{Object.entries(consumables.consumables).map(([id, consumable]) => {
|
{Object.entries(consumables.consumables).map(([id, consumable]) => {
|
||||||
if (!isCompatible(consumable.include, consumable.exclude)) {
|
if (
|
||||||
|
consumable.game_mode_exclusive ||
|
||||||
|
!isCompatible(consumable.include, consumable.exclude)
|
||||||
|
) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,7 +255,6 @@ function ConsumableEntry({ id, consumable }: ConsumableProps) {
|
|||||||
const isSelected = Tankopedia.use((state) =>
|
const isSelected = Tankopedia.use((state) =>
|
||||||
state.protagonist.consumables.includes(id),
|
state.protagonist.consumables.includes(id),
|
||||||
);
|
);
|
||||||
const tank = useDuel("protagonist", DuelModule.Tank);
|
|
||||||
const unwrap = useUnwrapper();
|
const unwrap = useUnwrapper();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -338,6 +418,7 @@ function EquipmentOption({
|
|||||||
return (
|
return (
|
||||||
<Tooltip tooltip={unwrap(specificEquipment.name)}>
|
<Tooltip tooltip={unwrap(specificEquipment.name)}>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
size="major"
|
||||||
color={isSelected ? undefined : "gray"}
|
color={isSelected ? undefined : "gray"}
|
||||||
variant={isSelected ? "surface" : "soft"}
|
variant={isSelected ? "surface" : "soft"}
|
||||||
data-selected={isSelected}
|
data-selected={isSelected}
|
||||||
|
|||||||
@@ -33,10 +33,28 @@ export const characteristics = {
|
|||||||
|
|
||||||
if (environment.equalize) coefficient *= equalizer.damage;
|
if (environment.equalize) coefficient *= equalizer.damage;
|
||||||
|
|
||||||
|
// tungsten shells
|
||||||
script("protagonist", "TungstenTip", (effect) => {
|
script("protagonist", "TungstenTip", (effect) => {
|
||||||
coefficient *= (effect.minDamageFactor + effect.maxDamageFactor) / 2;
|
coefficient *= (effect.minDamageFactor + effect.maxDamageFactor) / 2;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// reactive armor
|
||||||
|
script("antagonist", "ShieldKit", (effect) => {
|
||||||
|
if (shell.type === ShellType.SHELL_TYPE_HE) return;
|
||||||
|
coefficient *= 1 - effect.shieldCoef;
|
||||||
|
});
|
||||||
|
|
||||||
|
// dynamic armour system
|
||||||
|
script("antagonist", "ArmorMover", (effect) => {
|
||||||
|
coefficient *= 1 - effect.bonusValues!.damageReductionPercent / 100;
|
||||||
|
});
|
||||||
|
|
||||||
|
// spall liner
|
||||||
|
script("antagonist", "AntiHighExplosive", (effect) => {
|
||||||
|
if (shell.type !== ShellType.SHELL_TYPE_HE) return;
|
||||||
|
coefficient *= effect.bonusValues!.factor;
|
||||||
|
});
|
||||||
|
|
||||||
return coefficient * shell.armor_damage;
|
return coefficient * shell.armor_damage;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -46,19 +64,11 @@ export const characteristics = {
|
|||||||
compute({ shell, script }) {
|
compute({ shell, script }) {
|
||||||
let coefficient = 1;
|
let coefficient = 1;
|
||||||
|
|
||||||
|
// tungsten shells
|
||||||
script("protagonist", "TungstenTip", (effect) => {
|
script("protagonist", "TungstenTip", (effect) => {
|
||||||
coefficient *= (effect.minDamageFactor + effect.maxDamageFactor) / 2;
|
coefficient *= (effect.minDamageFactor + effect.maxDamageFactor) / 2;
|
||||||
});
|
});
|
||||||
|
|
||||||
script("antagonist", "ShieldKit", (effect) => {
|
|
||||||
if (shell.type === ShellType.SHELL_TYPE_HE) return;
|
|
||||||
coefficient *= 1 - effect.shieldCoef;
|
|
||||||
});
|
|
||||||
|
|
||||||
script("antagonist", "ArmorMover", (effect) => {
|
|
||||||
coefficient *= 1 - effect.bonusValues!.damageReductionPercent / 100;
|
|
||||||
});
|
|
||||||
|
|
||||||
return coefficient * shell.module_damage;
|
return coefficient * shell.module_damage;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import {
|
|||||||
import { coefficient } from "@blitzkit/core/src/blitzkit/coefficient";
|
import { coefficient } from "@blitzkit/core/src/blitzkit/coefficient";
|
||||||
import {
|
import {
|
||||||
CrewType,
|
CrewType,
|
||||||
ShellType,
|
|
||||||
TankClass,
|
TankClass,
|
||||||
type EngineDefinition,
|
type EngineDefinition,
|
||||||
type EquipmentDefinitions,
|
type EquipmentDefinitions,
|
||||||
@@ -24,7 +23,6 @@ import {
|
|||||||
type TurretDefinition,
|
type TurretDefinition,
|
||||||
} from "@blitzkit/protos";
|
} from "@blitzkit/protos";
|
||||||
import type { EquipmentMatrix } from "../../stores/duel";
|
import type { EquipmentMatrix } from "../../stores/duel";
|
||||||
import { SPALL_LINER_HE_DAMAGE_DELTA } from "./spallLiner";
|
|
||||||
import { defaultEqualizer } from "./tankToDuelMember";
|
import { defaultEqualizer } from "./tankToDuelMember";
|
||||||
|
|
||||||
export type TankCharacteristics = ReturnType<typeof tankCharacteristics>;
|
export type TankCharacteristics = ReturnType<typeof tankCharacteristics>;
|
||||||
@@ -168,10 +166,6 @@ export function tankCharacteristics(
|
|||||||
const intraClipCoefficient = coefficient([hasShellReloadBoost, -0.3]);
|
const intraClipCoefficient = coefficient([hasShellReloadBoost, -0.3]);
|
||||||
const burstShells = gun.burst?.count ?? 1;
|
const burstShells = gun.burst?.count ?? 1;
|
||||||
const burstInterShell = gun.burst?.interval;
|
const burstInterShell = gun.burst?.interval;
|
||||||
const armorDamageCoefficient = coefficient([
|
|
||||||
applySpallLiner && shell.type === ShellType.SHELL_TYPE_HE,
|
|
||||||
SPALL_LINER_HE_DAMAGE_DELTA,
|
|
||||||
]);
|
|
||||||
const assaultDamageCoefficient =
|
const assaultDamageCoefficient =
|
||||||
gun.assault_ranges && gun.assault_ranges.types.includes(shell.type)
|
gun.assault_ranges && gun.assault_ranges.types.includes(shell.type)
|
||||||
? (gun.assault_ranges.ranges.find(
|
? (gun.assault_ranges.ranges.find(
|
||||||
|
|||||||
@@ -59,6 +59,13 @@ export function computeCharacteristics(
|
|||||||
scriptsMap.protagonist.set(script["#text"], script);
|
scriptsMap.protagonist.set(script["#text"], script);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const id of states[side].provisions) {
|
||||||
|
if (!(id in scripts.provisions)) continue;
|
||||||
|
|
||||||
|
const script = scripts.provisions[id];
|
||||||
|
scriptsMap.protagonist.set(script["#text"], script);
|
||||||
|
}
|
||||||
|
|
||||||
for (const index in states[side].equipment) {
|
for (const index in states[side].equipment) {
|
||||||
const choice = states[side].equipment[index];
|
const choice = states[side].equipment[index];
|
||||||
const id = equipmentPreset.slots[index].options[choice];
|
const id = equipmentPreset.slots[index].options[choice];
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
import { api } from "../api/dynamic";
|
|
||||||
import { useAwait } from "./useAwait";
|
|
||||||
|
|
||||||
export function useConsumables() {
|
|
||||||
const consumables = useAwait(() => api.consumables(), "consumables");
|
|
||||||
return consumables;
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Duel } from '../stores/duel';
|
import { Tankopedia } from "../stores/tankopedia";
|
||||||
|
import type { DuelSide } from "./useEquipment";
|
||||||
|
|
||||||
export function useProvision(id: number) {
|
export function useProvision(side: DuelSide, id: number) {
|
||||||
const provisions = Duel.use((state) => state.protagonist.provisions);
|
return Tankopedia.use((state) => state[side].provisions.includes(id));
|
||||||
return provisions.includes(id);
|
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user