mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
Merge pull request #262 from Hitjack007/spall-liner-in-tankopdiea-model-viewer
This commit is contained in:
5 files changed
+155
-1
No files matched your search
@@ -18,6 +18,10 @@ import {
|
|||||||
import { degToRad } from "three/src/math/MathUtils.js";
|
import { degToRad } from "three/src/math/MathUtils.js";
|
||||||
import { hasEquipment } from "../../../../core/blitzkit/hasEquipment";
|
import { hasEquipment } from "../../../../core/blitzkit/hasEquipment";
|
||||||
import { jsxTree } from "../../../../core/blitzkit/jsxTree";
|
import { jsxTree } from "../../../../core/blitzkit/jsxTree";
|
||||||
|
import {
|
||||||
|
SPALL_LINER_HE_DAMAGE_DELTA,
|
||||||
|
SPALL_LINER_PROVISION_ID,
|
||||||
|
} from "../../../../core/blitzkit/spallLiner";
|
||||||
import { defaultEqualizer } from "../../../../core/blitzkit/tankToDuelMember";
|
import { defaultEqualizer } from "../../../../core/blitzkit/tankToDuelMember";
|
||||||
import { discardClippingPlane } from "../../../../core/three/discardClippingPlane";
|
import { discardClippingPlane } from "../../../../core/three/discardClippingPlane";
|
||||||
import { Duel } from "../../../../stores/duel";
|
import { Duel } from "../../../../stores/duel";
|
||||||
@@ -384,6 +388,13 @@ export function SpacedArmorSceneComponent({
|
|||||||
|
|
||||||
shot.damage *= antagonistEqualizer.damage;
|
shot.damage *= antagonistEqualizer.damage;
|
||||||
|
|
||||||
|
const hasSpallLiner = Duel.state.protagonist.provisions.includes(
|
||||||
|
SPALL_LINER_PROVISION_ID,
|
||||||
|
);
|
||||||
|
if (hasSpallLiner && shell.type === ShellType.SHELL_TYPE_HE) {
|
||||||
|
shot.damage *= 1 + SPALL_LINER_HE_DAMAGE_DELTA;
|
||||||
|
}
|
||||||
|
|
||||||
return shot;
|
return shot;
|
||||||
},
|
},
|
||||||
[camera],
|
[camera],
|
||||||
|
|||||||
+128
@@ -0,0 +1,128 @@
|
|||||||
|
import { alias, availableProvisions } from "@blitzkit/core";
|
||||||
|
import { ShuffleIcon } from "@radix-ui/react-icons";
|
||||||
|
import { Flex, IconButton, Popover } from "@radix-ui/themes";
|
||||||
|
import { api } from "../../../../../../core/blitzkit/api";
|
||||||
|
import { SPALL_LINER_PROVISION_ID } from "../../../../../../core/blitzkit/spallLiner";
|
||||||
|
import { useLocale } from "../../../../../../hooks/useLocale";
|
||||||
|
import { Duel } from "../../../../../../stores/duel";
|
||||||
|
import { Tankopedia } from "../../../../../../stores/tankopedia";
|
||||||
|
|
||||||
|
const provisionDefinitions = await api.provisionDefinitions();
|
||||||
|
|
||||||
|
export function SpallLinerSwitcher() {
|
||||||
|
const { tank, gun, provisions } = Duel.use((state) => state.protagonist);
|
||||||
|
const { unwrap } = useLocale();
|
||||||
|
const isSpallLinerActive = provisions.includes(SPALL_LINER_PROVISION_ID);
|
||||||
|
const hasSlotAvailable = provisions.length < tank.max_provisions;
|
||||||
|
|
||||||
|
const canEquipSpallLiner = availableProvisions(
|
||||||
|
tank,
|
||||||
|
gun,
|
||||||
|
provisionDefinitions,
|
||||||
|
).some((provision) => provision.id === SPALL_LINER_PROVISION_ID);
|
||||||
|
|
||||||
|
if (!canEquipSpallLiner) return null;
|
||||||
|
|
||||||
|
function equip(replacing?: number) {
|
||||||
|
Duel.mutate((draft) => {
|
||||||
|
if (replacing !== undefined) {
|
||||||
|
const provisions = draft.protagonist.provisions;
|
||||||
|
provisions[provisions.indexOf(replacing)] = SPALL_LINER_PROVISION_ID;
|
||||||
|
} else {
|
||||||
|
draft.protagonist.provisions.push(SPALL_LINER_PROVISION_ID);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Tankopedia.mutate((draft) => {
|
||||||
|
draft.shot = undefined;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function unequip() {
|
||||||
|
Duel.mutate((draft) => {
|
||||||
|
draft.protagonist.provisions = draft.protagonist.provisions.filter(
|
||||||
|
(id) => id !== SPALL_LINER_PROVISION_ID,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
Tankopedia.mutate((draft) => {
|
||||||
|
draft.shot = undefined;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const button = (
|
||||||
|
<IconButton
|
||||||
|
color={isSpallLinerActive ? undefined : "gray"}
|
||||||
|
variant="soft"
|
||||||
|
size={{ initial: "2", sm: "3" }}
|
||||||
|
radius="medium"
|
||||||
|
onClick={() => {
|
||||||
|
if (isSpallLinerActive) {
|
||||||
|
unequip();
|
||||||
|
} else if (hasSlotAvailable) {
|
||||||
|
equip();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
alt={unwrap(
|
||||||
|
provisionDefinitions.provisions[SPALL_LINER_PROVISION_ID].name!,
|
||||||
|
)}
|
||||||
|
src={alias("api", `/icons/provisions/${SPALL_LINER_PROVISION_ID}.webp`)}
|
||||||
|
style={{
|
||||||
|
width: "50%",
|
||||||
|
height: "50%",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</IconButton>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isSpallLinerActive || hasSlotAvailable) return button;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover.Root>
|
||||||
|
<Popover.Trigger>{button}</Popover.Trigger>
|
||||||
|
|
||||||
|
<Popover.Content>
|
||||||
|
<Flex gap="2" align="center">
|
||||||
|
<img
|
||||||
|
alt={unwrap(
|
||||||
|
provisionDefinitions.provisions[SPALL_LINER_PROVISION_ID].name!,
|
||||||
|
)}
|
||||||
|
src={alias(
|
||||||
|
"api",
|
||||||
|
`/icons/provisions/${SPALL_LINER_PROVISION_ID}.webp`,
|
||||||
|
)}
|
||||||
|
style={{
|
||||||
|
width: "1.25em",
|
||||||
|
height: "1.25em",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ShuffleIcon color="var(--gray-9)" />
|
||||||
|
|
||||||
|
<Flex gap="2">
|
||||||
|
{provisions.map((id) => (
|
||||||
|
<Popover.Close key={id}>
|
||||||
|
<IconButton
|
||||||
|
color="gray"
|
||||||
|
variant="soft"
|
||||||
|
size="3"
|
||||||
|
radius="medium"
|
||||||
|
onClick={() => equip(id)}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
alt={unwrap(provisionDefinitions.provisions[id].name!)}
|
||||||
|
src={alias("api", `/icons/provisions/${id}.webp`)}
|
||||||
|
style={{
|
||||||
|
width: "50%",
|
||||||
|
height: "50%",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</IconButton>
|
||||||
|
</Popover.Close>
|
||||||
|
))}
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
</Popover.Content>
|
||||||
|
</Popover.Root>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -46,6 +46,7 @@ import { TankSearch } from "../../../../TankSearch";
|
|||||||
import { CustomShellButton } from "./components/CustomShellButton";
|
import { CustomShellButton } from "./components/CustomShellButton";
|
||||||
import { DynamicArmorSwitcher } from "./components/DynamicArmorSwitcher";
|
import { DynamicArmorSwitcher } from "./components/DynamicArmorSwitcher";
|
||||||
import { QuickInputs } from "./components/QuickInputs";
|
import { QuickInputs } from "./components/QuickInputs";
|
||||||
|
import { SpallLinerSwitcher } from "./components/SpallLinerSwitcher";
|
||||||
import { Thicknesses } from "./components/Thicknesses";
|
import { Thicknesses } from "./components/Thicknesses";
|
||||||
|
|
||||||
type OptionsProps = MaybeSkeletonComponentProps & {
|
type OptionsProps = MaybeSkeletonComponentProps & {
|
||||||
@@ -278,6 +279,8 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
|
||||||
|
<SpallLinerSwitcher />
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
{!skeleton && (
|
{!skeleton && (
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
export const SPALL_LINER_PROVISION_ID = 101;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matches the in-game reduction Spall Liner grants against HE splash
|
||||||
|
* damage. Shared between the characteristics panel and the Dynamic
|
||||||
|
* Armor 3D viewer's damage readout so the figure isn't duplicated.
|
||||||
|
*/
|
||||||
|
export const SPALL_LINER_HE_DAMAGE_DELTA = -0.2;
|
||||||
@@ -22,6 +22,7 @@ import {
|
|||||||
} from "@blitzkit/core";
|
} from "@blitzkit/core";
|
||||||
import { coefficient } from "@blitzkit/core/src/blitzkit/coefficient";
|
import { coefficient } from "@blitzkit/core/src/blitzkit/coefficient";
|
||||||
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>;
|
||||||
@@ -170,7 +171,10 @@ export function tankCharacteristics(
|
|||||||
coefficient(
|
coefficient(
|
||||||
[applyReactiveArmor && shell.type !== ShellType.SHELL_TYPE_HE, -0.27],
|
[applyReactiveArmor && shell.type !== ShellType.SHELL_TYPE_HE, -0.27],
|
||||||
[applyDynamicArmor, -0.1],
|
[applyDynamicArmor, -0.1],
|
||||||
[applySpallLiner && shell.type === ShellType.SHELL_TYPE_HE, -0.2],
|
[
|
||||||
|
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)
|
||||||
|
|||||||
Reference in new issue
Block a user