mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
feat: better modules
This commit is contained in:
1 parent
7af5d5f465
commit
d0743b54e0
3 files changed
+170
-48
No files matched your search
@@ -35,6 +35,7 @@ import {
|
|||||||
TankDefinitionPrice,
|
TankDefinitionPrice,
|
||||||
TankDefinitions,
|
TankDefinitions,
|
||||||
Tier,
|
Tier,
|
||||||
|
Unlock,
|
||||||
} from '../../src/core/blitzkit/tankDefinitions';
|
} from '../../src/core/blitzkit/tankDefinitions';
|
||||||
import { DATA, POI } from './constants';
|
import { DATA, POI } from './constants';
|
||||||
import { Avatar } from './skillIcons';
|
import { Avatar } from './skillIcons';
|
||||||
@@ -86,7 +87,7 @@ type VehicleDefinitionArmor = Record<
|
|||||||
number | { vehicleDamageFactor: 0; '#text': number }
|
number | { vehicleDamageFactor: 0; '#text': number }
|
||||||
>;
|
>;
|
||||||
interface UnlocksInner {
|
interface UnlocksInner {
|
||||||
cost: number;
|
cost: number | string;
|
||||||
'#text': number;
|
'#text': number;
|
||||||
}
|
}
|
||||||
type Unlocks = {
|
type Unlocks = {
|
||||||
@@ -477,7 +478,17 @@ export async function definitions(production: boolean) {
|
|||||||
return {
|
return {
|
||||||
type: typeTyped,
|
type: typeTyped,
|
||||||
id: toUniqueId(nation, rawId!),
|
id: toUniqueId(nation, rawId!),
|
||||||
};
|
cost: {
|
||||||
|
type:
|
||||||
|
typeof item.cost === 'number'
|
||||||
|
? 'xp'
|
||||||
|
: item.cost.split(':')[0],
|
||||||
|
value:
|
||||||
|
typeof item.cost === 'number'
|
||||||
|
? item.cost
|
||||||
|
: Number(item.cost.split(':')[1]),
|
||||||
|
},
|
||||||
|
} satisfies Unlock;
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.flat();
|
.flat();
|
||||||
@@ -948,7 +959,7 @@ export async function definitions(production: boolean) {
|
|||||||
const currentTank = tankDefinitions[tankId];
|
const currentTank = tankDefinitions[tankId];
|
||||||
const successorId = toUniqueId(nation, tankListEntry.id);
|
const successorId = toUniqueId(nation, tankListEntry.id);
|
||||||
|
|
||||||
tankXps.set(successorId, vehicle.cost);
|
tankXps.set(successorId, vehicle.cost as number);
|
||||||
|
|
||||||
if (currentTank.successors === undefined) {
|
if (currentTank.successors === undefined) {
|
||||||
currentTank.successors = [];
|
currentTank.successors = [];
|
||||||
@@ -962,7 +973,7 @@ export async function definitions(production: boolean) {
|
|||||||
case 'gun': {
|
case 'gun': {
|
||||||
gunXps.set(
|
gunXps.set(
|
||||||
toUniqueId(nation, gunList.root.ids[vehicle['#text']]),
|
toUniqueId(nation, gunList.root.ids[vehicle['#text']]),
|
||||||
vehicle.cost,
|
vehicle.cost as number,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -970,7 +981,7 @@ export async function definitions(production: boolean) {
|
|||||||
case 'turret': {
|
case 'turret': {
|
||||||
turretXps.set(
|
turretXps.set(
|
||||||
toUniqueId(nation, turretList.root.ids[vehicle['#text']]),
|
toUniqueId(nation, turretList.root.ids[vehicle['#text']]),
|
||||||
vehicle.cost,
|
vehicle.cost as number,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -978,7 +989,7 @@ export async function definitions(production: boolean) {
|
|||||||
case 'engine': {
|
case 'engine': {
|
||||||
engineXps.set(
|
engineXps.set(
|
||||||
toUniqueId(nation, enginesList.root.ids[vehicle['#text']]),
|
toUniqueId(nation, enginesList.root.ids[vehicle['#text']]),
|
||||||
vehicle.cost,
|
vehicle.cost as number,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -986,7 +997,7 @@ export async function definitions(production: boolean) {
|
|||||||
case 'chassis': {
|
case 'chassis': {
|
||||||
trackXps.set(
|
trackXps.set(
|
||||||
toUniqueId(nation, chassisList.root.ids[vehicle['#text']]),
|
toUniqueId(nation, chassisList.root.ids[vehicle['#text']]),
|
||||||
vehicle.cost,
|
vehicle.cost as number,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,106 @@
|
|||||||
import { Flex, Heading } from '@radix-ui/themes';
|
import { Flex, Heading, IconButton, Text } from '@radix-ui/themes';
|
||||||
|
import Link from 'next/link';
|
||||||
import { use } from 'react';
|
import { use } from 'react';
|
||||||
import { ModuleButton } from '../../../../../../components/ModuleButtons/ModuleButton';
|
|
||||||
import PageWrapper from '../../../../../../components/PageWrapper';
|
import PageWrapper from '../../../../../../components/PageWrapper';
|
||||||
|
import { asset } from '../../../../../../core/blitzkit/asset';
|
||||||
import {
|
import {
|
||||||
ModuleDefinition,
|
ModuleDefinition,
|
||||||
ModuleType,
|
ModuleType,
|
||||||
TankDefinition,
|
TankDefinition,
|
||||||
tankDefinitions,
|
tankDefinitions,
|
||||||
|
Tier,
|
||||||
Unlock,
|
Unlock,
|
||||||
} from '../../../../../../core/blitzkit/tankDefinitions';
|
} from '../../../../../../core/blitzkit/tankDefinitions';
|
||||||
import { TIER_ROMAN_NUMERALS } from '../../../../../../core/blitzkit/tankDefinitions/constants';
|
import { TIER_ROMAN_NUMERALS } from '../../../../../../core/blitzkit/tankDefinitions/constants';
|
||||||
|
import { tankIcon } from '../../../../../../core/blitzkit/tankIcon';
|
||||||
import { mutateDuel, useDuel } from '../../../../../../stores/duel';
|
import { mutateDuel, useDuel } from '../../../../../../stores/duel';
|
||||||
import { TreeArrow } from './TechTreeSection';
|
import { TreeArrow } from './TechTreeSection';
|
||||||
|
|
||||||
|
function ModuleButton({
|
||||||
|
tier,
|
||||||
|
selected,
|
||||||
|
unlock,
|
||||||
|
onClick,
|
||||||
|
}: {
|
||||||
|
unlock: Unlock;
|
||||||
|
tier: Tier;
|
||||||
|
selected: boolean;
|
||||||
|
onClick: () => void;
|
||||||
|
}) {
|
||||||
|
const numberFormat = Intl.NumberFormat(undefined, {
|
||||||
|
notation: 'compact',
|
||||||
|
maximumFractionDigits: 0,
|
||||||
|
});
|
||||||
|
const button = (
|
||||||
|
<IconButton
|
||||||
|
size="4"
|
||||||
|
radius="small"
|
||||||
|
variant={selected ? 'surface' : 'soft'}
|
||||||
|
color={selected ? undefined : 'gray'}
|
||||||
|
onClick={onClick}
|
||||||
|
style={{
|
||||||
|
position: 'relative',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
size="1"
|
||||||
|
color="gray"
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
left: 4,
|
||||||
|
top: 4,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{TIER_ROMAN_NUMERALS[tier]}
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
{unlock.cost.value > 0 && (
|
||||||
|
<Flex
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
left: '50%',
|
||||||
|
bottom: 4,
|
||||||
|
transform: 'translateX(-50%)',
|
||||||
|
}}
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
|
<Text size="1" color="gray">
|
||||||
|
{numberFormat.format(unlock.cost.value)}
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<img
|
||||||
|
src={asset(`icons/currencies/${unlock.cost.type}.webp`)}
|
||||||
|
style={{
|
||||||
|
width: 12,
|
||||||
|
height: 12,
|
||||||
|
objectFit: 'contain',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<img
|
||||||
|
src={
|
||||||
|
unlock.type === 'vehicle'
|
||||||
|
? tankIcon(unlock.id)
|
||||||
|
: asset(`icons/modules/${unlock.type}.webp`)
|
||||||
|
}
|
||||||
|
style={{
|
||||||
|
width: 32,
|
||||||
|
height: 32,
|
||||||
|
objectFit: 'contain',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</IconButton>
|
||||||
|
);
|
||||||
|
|
||||||
|
return unlock.type === 'vehicle' ? (
|
||||||
|
<Link href={`/tools/tankopedia/${unlock.id}`}>{button}</Link>
|
||||||
|
) : (
|
||||||
|
button
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function ModulesSection() {
|
export function ModulesSection() {
|
||||||
const awaitedTankDefinitions = use(tankDefinitions);
|
const awaitedTankDefinitions = use(tankDefinitions);
|
||||||
const tank = useDuel((state) => state.protagonist!.tank);
|
const tank = useDuel((state) => state.protagonist!.tank);
|
||||||
@@ -92,21 +180,17 @@ export function ModulesSection() {
|
|||||||
turret.guns.some((gun) => gun.id === unlock.id),
|
turret.guns.some((gun) => gun.id === unlock.id),
|
||||||
)!
|
)!
|
||||||
.guns.find((gun) => gun.id === unlock.id)!;
|
.guns.find((gun) => gun.id === unlock.id)!;
|
||||||
|
} else if (unlock.type === 'vehicle') {
|
||||||
|
module = awaitedTankDefinitions[unlock.id];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (module === undefined) return null;
|
if (module === undefined) return null;
|
||||||
|
|
||||||
const unlocksFiltered = (module as ModuleDefinition).unlocks?.filter(
|
|
||||||
({ type }) => type !== 'vehicle',
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex align="center" gap="2">
|
<Flex align="center" gap="2">
|
||||||
<ModuleButton
|
<ModuleButton
|
||||||
first
|
unlock={unlock}
|
||||||
last
|
tier={module.tier}
|
||||||
module={unlock.type}
|
|
||||||
discriminator={TIER_ROMAN_NUMERALS[module.tier]}
|
|
||||||
selected={
|
selected={
|
||||||
(unlock.type === 'turret'
|
(unlock.type === 'turret'
|
||||||
? turret.id
|
? turret.id
|
||||||
@@ -121,7 +205,7 @@ export function ModulesSection() {
|
|||||||
onClick={() => setByUnlock(unlock)}
|
onClick={() => setByUnlock(unlock)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{unlocksFiltered && unlocksFiltered.length > 0 && (
|
{(module as ModuleDefinition).unlocks && (
|
||||||
<>
|
<>
|
||||||
<TreeArrow style={{ width: 32 }} />
|
<TreeArrow style={{ width: 32 }} />
|
||||||
{tree(type, (module as ModuleDefinition).unlocks!)}
|
{tree(type, (module as ModuleDefinition).unlocks!)}
|
||||||
@@ -141,12 +225,20 @@ export function ModulesSection() {
|
|||||||
<Flex direction="column" gap="2">
|
<Flex direction="column" gap="2">
|
||||||
<Flex align="center" gap="2">
|
<Flex align="center" gap="2">
|
||||||
<ModuleButton
|
<ModuleButton
|
||||||
module="turret"
|
unlock={{
|
||||||
first
|
type: 'turret',
|
||||||
last
|
id: turret0.id,
|
||||||
discriminator={TIER_ROMAN_NUMERALS[turret0.tier]}
|
cost: { type: 'xp', value: 0 },
|
||||||
|
}}
|
||||||
|
tier={turret0.tier}
|
||||||
selected={turret0.id === turret.id}
|
selected={turret0.id === turret.id}
|
||||||
onClick={() => setByUnlock({ id: turret0.id, type: 'turret' })}
|
onClick={() =>
|
||||||
|
setByUnlock({
|
||||||
|
id: turret0.id,
|
||||||
|
type: 'turret',
|
||||||
|
cost: { type: 'xp', value: 0 },
|
||||||
|
})
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{turret0.unlocks && (
|
{turret0.unlocks && (
|
||||||
@@ -159,12 +251,20 @@ export function ModulesSection() {
|
|||||||
|
|
||||||
<Flex align="center" gap="2">
|
<Flex align="center" gap="2">
|
||||||
<ModuleButton
|
<ModuleButton
|
||||||
module="gun"
|
unlock={{
|
||||||
first
|
type: 'gun',
|
||||||
last
|
id: gun0.id,
|
||||||
discriminator={TIER_ROMAN_NUMERALS[gun0.tier]}
|
cost: { type: 'xp', value: 0 },
|
||||||
|
}}
|
||||||
|
tier={gun0.tier}
|
||||||
selected={gun0.id === gun.id}
|
selected={gun0.id === gun.id}
|
||||||
onClick={() => setByUnlock({ id: gun0.id, type: 'gun' })}
|
onClick={() =>
|
||||||
|
setByUnlock({
|
||||||
|
id: gun0.id,
|
||||||
|
type: 'gun',
|
||||||
|
cost: { type: 'xp', value: 0 },
|
||||||
|
})
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{gun0.unlocks && (
|
{gun0.unlocks && (
|
||||||
@@ -177,12 +277,20 @@ export function ModulesSection() {
|
|||||||
|
|
||||||
<Flex align="center" gap="2">
|
<Flex align="center" gap="2">
|
||||||
<ModuleButton
|
<ModuleButton
|
||||||
module="engine"
|
unlock={{
|
||||||
first
|
type: 'engine',
|
||||||
last
|
id: engine0.id,
|
||||||
discriminator={TIER_ROMAN_NUMERALS[engine0.tier]}
|
cost: { type: 'xp', value: 0 },
|
||||||
|
}}
|
||||||
|
tier={engine0.tier}
|
||||||
selected={engine0.id === engine.id}
|
selected={engine0.id === engine.id}
|
||||||
onClick={() => setByUnlock({ id: engine0.id, type: 'engine' })}
|
onClick={() =>
|
||||||
|
setByUnlock({
|
||||||
|
id: engine0.id,
|
||||||
|
type: 'engine',
|
||||||
|
cost: { type: 'xp', value: 0 },
|
||||||
|
})
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{engine0.unlocks && (
|
{engine0.unlocks && (
|
||||||
@@ -195,12 +303,20 @@ export function ModulesSection() {
|
|||||||
|
|
||||||
<Flex align="center" gap="2">
|
<Flex align="center" gap="2">
|
||||||
<ModuleButton
|
<ModuleButton
|
||||||
module="chassis"
|
unlock={{
|
||||||
first
|
type: 'chassis',
|
||||||
last
|
id: track0.id,
|
||||||
discriminator={TIER_ROMAN_NUMERALS[track0.tier]}
|
cost: { type: 'xp', value: 0 },
|
||||||
|
}}
|
||||||
|
tier={track0.tier}
|
||||||
selected={track0.id === track.id}
|
selected={track0.id === track.id}
|
||||||
onClick={() => setByUnlock({ id: track0.id, type: 'chassis' })}
|
onClick={() =>
|
||||||
|
setByUnlock({
|
||||||
|
id: track0.id,
|
||||||
|
type: 'chassis',
|
||||||
|
cost: { type: 'xp', value: 0 },
|
||||||
|
})
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{track0.unlocks && (
|
{track0.unlocks && (
|
||||||
@@ -210,16 +326,6 @@ export function ModulesSection() {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
{/* <Heading>gun0</Heading>
|
|
||||||
{tank.turrets[0].guns[0].unlocks &&
|
|
||||||
tree('gun', tank.turrets[0].guns[0].unlocks)}
|
|
||||||
|
|
||||||
<Heading>engine0</Heading>
|
|
||||||
{tank.engines[0].unlocks && tree('engine', tank.engines[0].unlocks)}
|
|
||||||
|
|
||||||
<Heading>track0</Heading>
|
|
||||||
{tank.tracks[0].unlocks && tree('chassis', tank.tracks[0].unlocks)} */}
|
|
||||||
</Flex>
|
</Flex>
|
||||||
</PageWrapper>
|
</PageWrapper>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -18,6 +18,11 @@ export type ModuleType = 'vehicle' | 'engine' | 'chassis' | 'turret' | 'gun';
|
|||||||
export interface Unlock {
|
export interface Unlock {
|
||||||
type: ModuleType;
|
type: ModuleType;
|
||||||
id: number;
|
id: number;
|
||||||
|
cost: {
|
||||||
|
// string being the season coins
|
||||||
|
type: 'xp' | string;
|
||||||
|
value: number;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
export interface Crew {
|
export interface Crew {
|
||||||
type: CrewMember;
|
type: CrewMember;
|
||||||
|
|||||||
Reference in new issue
Block a user