feat: start of more attrs

This commit is contained in:
TrèsAbhi committed 2025-12-18 16:12:01 -06:00
1 parent 6ce1135835
commit 7b830f5a71
6 files changed
+128 -49

No files matched your search

@@ -3,50 +3,83 @@ import {
TankAttributeChange_AttributeName,
} from "@protos/blitz_static_tank_upgrade_single_stage";
export type CharacteristicOutput = number | string | null;
export type CharacteristicOutput = number | null;
interface Characteristic {
value: (helpers: {
characteristic: (name: CharacteristicName) => CharacteristicOutput;
attribute: (name: TankAttributeChange_AttributeName) => number;
shell: (name: ShellUpgrageSingleChange_AttributeName) => number;
}) => CharacteristicOutput;
}
type Characteristic = (helpers: {
characteristic: (name: CharacteristicName) => CharacteristicOutput;
attribute: (name: TankAttributeChange_AttributeName) => number;
shell: (name: ShellUpgrageSingleChange_AttributeName) => number;
}) => CharacteristicOutput;
export type CharacteristicName = keyof typeof characteristics;
export enum GunType {
Regular,
AutoLoader,
AutoReloader,
}
export const characteristics = {
gun_type: {
value({ attribute }) {
const isPump = attribute(
TankAttributeChange_AttributeName.ATTRIBUTE_NAME_IS_PUMP
);
gun_type({ attribute }) {
const isPump = attribute(
TankAttributeChange_AttributeName.ATTRIBUTE_NAME_IS_PUMP
);
if (isPump === 1) return "auto_reloader";
if (isPump === 1) return GunType.AutoReloader;
const clipSize = attribute(
TankAttributeChange_AttributeName.ATTRIBUTE_NAME_CLIP_SIZE
);
const clipSize = attribute(
TankAttributeChange_AttributeName.ATTRIBUTE_NAME_CLIP_SIZE
);
if (clipSize === 1) return "regular";
if (clipSize === 1) return GunType.Regular;
return "auto_loader";
},
return GunType.AutoLoader;
},
damage: {
value({ shell }) {
return shell(
ShellUpgrageSingleChange_AttributeName.ATTRIBUTE_NAME_ARMOR_DAMAGE
);
},
damage({ shell }) {
return shell(
ShellUpgrageSingleChange_AttributeName.ATTRIBUTE_NAME_ARMOR_DAMAGE
);
},
dpm: {
value({ characteristic }) {
const damage = characteristic("damage");
module_damage({ shell }) {
return shell(
ShellUpgrageSingleChange_AttributeName.ATTRIBUTE_NAME_MODULE_DAMAGE
);
},
return 1;
},
penetration({ shell }) {
return shell(
ShellUpgrageSingleChange_AttributeName.ATTRIBUTE_NAME_PIERCING_POWER
);
},
reload({ characteristic, attribute }) {
const gunType = characteristic("gun_type")!;
if (gunType !== GunType.Regular) return null;
return attribute(
TankAttributeChange_AttributeName.ATTRIBUTE_NAME_RELOAD_TIME
);
},
dpm({ characteristic }) {
let dps: number;
const gunType = characteristic("gun_type")!;
const damage = characteristic("damage")!;
switch (gunType) {
case GunType.Regular: {
const reload = characteristic("reload")!;
dps = damage / reload;
break;
}
default:
throw new Error("Unknown gun type");
}
return 60 * dps;
},
} satisfies Record<string, Characteristic>;
@@ -1,14 +1,28 @@
import type { CharacteristicName } from "./characteristics";
export enum BetterDirection {
Higher,
Lower,
}
type CharacteristicsOrder = {
group: string;
order: CharacteristicName[];
order: {
name: CharacteristicName;
decimals?: number;
}[];
}[];
export const characteristicsGroups = [
{
group: "firepower",
order: ["gun_type", "dpm"],
order: [
{ name: "gun_type" },
{ name: "dpm", decimals: 0 },
{ name: "damage" },
{ name: "module_damage" },
{ name: "penetration" },
],
},
{
group: "maneuverability",
@@ -8,10 +8,11 @@ import {
type CharacteristicName,
type CharacteristicOutput,
} from "./characteristics";
import type { TankState } from "./tankState";
export function renderCharacteristics(
export function computeCharacteristics(
parameters: StageParameters,
shellId: number
state: TankState
) {
const computed: Partial<Record<CharacteristicName, CharacteristicOutput>> =
{};
@@ -34,7 +35,7 @@ export function renderCharacteristics(
function shell(name: ShellUpgrageSingleChange_AttributeName) {
const shell = parameters.shells_upgrades.find(
(shell) => shell.shell_id === name
(shell) => shell.shell_id === state.shell
);
if (!shell) throw new Error(`Shell ${name} not found`);
@@ -50,7 +51,9 @@ export function renderCharacteristics(
for (const _name in characteristics) {
const name = _name as CharacteristicName;
const _characteristic = characteristics[name];
const value = _characteristic.value({ characteristic, attribute, shell });
const value = characteristics[name]({ characteristic, attribute, shell });
computed[name] = value;
}
return computed as Record<CharacteristicName, CharacteristicOutput>;
}
@@ -0,0 +1,5 @@
export interface TankState {
stage: number;
shell: number;
}
@@ -2,7 +2,8 @@ import { times } from "lodash-es";
import { Suspense, useMemo } from "react";
import { api } from "../../../core/api/dynamic";
import { aggregateStageParameters } from "../../../core/tankopedia/aggregateStageParameters";
import { characteristics } from "../../../core/tankopedia/characteristics";
import { characteristicsGroups } from "../../../core/tankopedia/characteristicsGroups";
import { computeCharacteristics } from "../../../core/tankopedia/computeCharacteristics";
import { useAwait } from "../../../hooks/useAwait";
import { Tankopedia } from "../../../stores/tankopedia";
@@ -17,11 +18,19 @@ export function Page({ id }: { id: string }) {
function Content({ id }: { id: string }) {
const tank = useAwait(() => api.tank(id), `tank-${id}`);
const stage = Tankopedia.use((state) => state.stage);
const protagonist = Tankopedia.use((state) => state.protagonist);
const parameters = useMemo(
() =>
aggregateStageParameters(tank.base_stats!, tank.upgrade_stages, stage),
[stage]
aggregateStageParameters(
tank.base_stats!,
tank.upgrade_stages,
protagonist.shell
),
[protagonist]
);
const characteristics = useMemo(
() => computeCharacteristics(parameters, protagonist),
[protagonist]
);
return (
@@ -32,23 +41,35 @@ function Content({ id }: { id: string }) {
key={index}
onClick={() => {
Tankopedia.mutate((draft) => {
draft.stage = index;
draft.protagonist.stage = index;
});
}}
>
Stage {index} {index === stage && "(selected)"}
Stage {index} {index === protagonist.stage && "(selected)"}
</button>
))}
</div>
{characteristicsOrder.map(({ group, order }) => (
{characteristicsGroups.map(({ group, order }) => (
<>
<h2>{group}</h2>
{order.map((name) => {
{order.map(({ name, decimals }) => {
const characteristic = characteristics[name];
return <span>{}</span>;
if (characteristic === null) return null;
let rendered: string | number = characteristic;
if (typeof rendered === "number") {
if (decimals !== undefined) rendered = rendered.toFixed(decimals);
}
return (
<p>
{name}: {rendered}
</p>
);
})}
</>
))}
+5 -2
View File
@@ -1,7 +1,10 @@
import { Varuna } from "varuna";
import type { TankState } from "../core/tankopedia/tankState";
interface Tankopedia {
stage: number;
protagonist: TankState;
}
export const Tankopedia = new Varuna<Tankopedia>({ stage: 0 });
export const Tankopedia = new Varuna<Tankopedia>({
protagonist: { stage: 0, shell: 0 },
});