feat: some aggregation

Co-authored-by: Copilot <[email protected]>
This commit is contained in:
TrèsAbhiandCopilot committed 2026-04-28 21:22:50 -05:00
1 parent 4b7b8b3392
commit 018aa33a7f
3 files changed
+262 -228

No files matched your search

@@ -1,4 +1,4 @@
import { TankAttributeChange } from "@protos/game/proto/legacy/auto_items";
import type { TankCatalogComponent } from "@blitzkit/closed/protos/game/proto/legacy/blitz_static_tank_component";
import { StandardSinglePrice } from "@protos/game/proto/legacy/blitz_static_standard_single_price";
import { PenetrationGroup } from "@protos/game/proto/legacy/blitz_static_tank_penetration_group";
import {
@@ -7,13 +7,14 @@ import {
ShellUpgrade,
ShellUpgradeSingleChange,
StageParameters,
TankAttributeChange,
TankAttributeChange_AttributeName,
TankAttributeChange_Modifier,
VisualChanges,
} from "@protos/game/proto/legacy/blitz_static_tank_upgrade_single_stage";
function patch(stage0: StageParameters, stage1: StageParameters) {
if (stage1.stage_number !== ++stage0.stage_number) {
if (stage1.number !== ++stage0.number) {
throw new Error("Change stage number must be 1 greater than base");
}
@@ -181,26 +182,21 @@ function patch(stage0: StageParameters, stage1: StageParameters) {
}
}
export function aggregateStageParameters(
base: StageParameters,
stages: StageParameters[],
export function aggregateParameters(
tank: TankCatalogComponent,
upgrades: Record<string, number>,
) {
const stage0 = StageParameters.create({ ...base, stage_number: 0 });
const stage0 = StageParameters.create({});
/**
* Bug in Reforged: base stats are stage 1, so is the first upgrade. Manually
* setting it to stage 0 below and if the bug's been fixed, throw an error
* in dev mode to allow clean up in the future. Also, update the initial
* stage number to -1.
*/
for (const line of tank.upgrade_lines) {
const stage = upgrades[line.name];
if (base.stage_number === 0 && import.meta.env.DEV) {
throw new Error(
"Base stats are now stage 0. In-game bug fixed, please remove hack.",
);
stage0.number = 0;
for (let i = 0; i < stage; i++) {
patch(stage0, line.stages[i]);
}
}
stages.forEach((stage) => patch(stage0, stage));
return stage0;
}
@@ -3,7 +3,7 @@ import {
ShellUpgradeSingleChange_AttributeName,
TankAttributeChange_AttributeName,
} from "@protos/game/proto/legacy/blitz_static_tank_upgrade_single_stage";
import { aggregateStageParameters } from "./aggregateStageParameters";
import { aggregateStageParameters } from "./aggregateParameters";
import {
characteristics,
type CharacteristicName,
@@ -1,15 +1,15 @@
import { times } from "lodash-es";
import { Suspense, useMemo } from "react";
import { TankopediaCharacteristic } from "../../../../components/TankopediaCharacteristic";
import { TankopediaToy } from "../../../../components/TankopediaToy";
import { Grade } from "@blitzkit/closed/protos/game/proto/legacy/blitz_static_standard_grades_enum";
import {
StageParameters_StageType,
TankAttributeChange_AttributeName,
} from "@blitzkit/closed/protos/game/proto/legacy/blitz_static_tank_upgrade_single_stage";
import { Suspense } from "react";
import { api } from "../../../../core/api/dynamic";
import { characteristicsOrder } from "../../../../core/tankopedia/characteristicsOrder";
import { computeCharacteristics } from "../../../../core/tankopedia/computeCharacteristics";
import { TerrainHardness } from "../../../../core/tankopedia/tankState";
import { aggregateParameters } from "../../../../core/tankopedia/aggregateParameters";
import { useAwait } from "../../../../hooks/useAwait";
import { useGameStrings } from "../../../../hooks/useGameStrings";
import { LocaleProvider } from "../../../../hooks/useLocale";
import { Tankopedia, TankopediaCompare } from "../../../../stores/tankopedia";
import { Tankopedia } from "../../../../stores/tankopedia";
interface PageProps {
id: string;
@@ -43,21 +43,44 @@ function Content() {
`tank-${protagonist.id}`,
);
const parameters = aggregateParameters(
protagonistTank.tank!,
protagonist.upgrades,
);
return (
<>
<h1>{protagonist.id}</h1>
<br />
<h2>upgrades</h2>
{protagonistTank.tank!.upgrade_lines.map((line) => {
return (
<>
<h2>
<h3>
{line.name} ({protagonist.upgrades[line.name]})
</h2>
</h3>
<div style={{ display: "flex", flexWrap: "wrap", gap: 8 }}>
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
gap: 8,
}}
>
{line.stages.map((stage, index) => {
if (stage.custom_picture) {
throw new Error("Custom picture not supported");
}
if (stage.required_upgrades.length > 0) {
console.log(stage.required_upgrades);
throw new Error("Required upgrades not supported");
}
return (
<button
key={stage.tech_name}
@@ -67,7 +90,9 @@ function Content() {
});
}}
>
{protagonist.upgrades[line.name] === index && "✓"}{" "}
{protagonist.upgrades[line.name] === index && "✓"}
{Grade[stage.grade]}
{StageParameters_StageType[stage.stage_type]}
{tankEntityGameStrings[stage.display_name]}
</button>
);
@@ -76,230 +101,243 @@ function Content() {
</>
);
})}
<h2>parameters</h2>
<h3>attributes</h3>
{parameters.attributes.map((attribute) => {
return (
<span>
{TankAttributeChange_AttributeName[attribute.attribute_name]}:{" "}
{attribute.value}
</span>
);
})}
</>
);
const compare = Tankopedia.use((state) => state.compare);
const characteristics = useMemo(
() => computeCharacteristics(id, tank, protagonist),
[protagonist],
);
// const otherCharacteristics = useMemo(
// () =>
// tankList.list
// .filter((tank) => tank.id !== id)
// .map(({ id }) => {
// const state = { ...protagonist };
// const tank = otherTanks.tanks[id];
// return computeCharacteristics(
// id,
// tank.tank!,
// tank.compensation!,
// state
// );
// }),
// [protagonist]
// const compare = Tankopedia.use((state) => state.compare);
// const characteristics = useMemo(
// () => computeCharacteristics(id, tank, protagonist),
// [protagonist],
// );
// // const otherCharacteristics = useMemo(
// // () =>
// // tankList.list
// // .filter((tank) => tank.id !== id)
// // .map(({ id }) => {
// // const state = { ...protagonist };
// // const tank = otherTanks.tanks[id];
return (
<>
<h1>{id}</h1>
// // return computeCharacteristics(
// // id,
// // tank.tank!,
// // tank.compensation!,
// // state
// // );
// // }),
// // [protagonist]
// // );
<div style={{ display: "flex", flexWrap: "wrap", gap: "1rem" }}>
{tank.tank?.feature_screens?.tags.map((tag) => (
<span>{tag.value}</span>
))}
</div>
// return (
// <>
// <h1>{id}</h1>
<br />
// <div style={{ display: "flex", flexWrap: "wrap", gap: "1rem" }}>
// {tank.tank?.feature_screens?.tags.map((tag) => (
// <span>{tag.value}</span>
// ))}
// </div>
<div style={{ display: "flex", flexWrap: "wrap" }}>
{times(tank.tank!.upgrade_stages.length + 1, (index) => (
<button
key={index}
onClick={() => {
Tankopedia.mutate((draft) => {
draft.protagonist.stage = index;
});
}}
>
Stage {index} {index === protagonist.stage && "(selected)"}
</button>
))}
</div>
// <br />
<br />
// <div style={{ display: "flex", flexWrap: "wrap" }}>
// {times(tank.tank!.upgrade_stages.length + 1, (index) => (
// <button
// key={index}
// onClick={() => {
// Tankopedia.mutate((draft) => {
// draft.protagonist.stage = index;
// });
// }}
// >
// Stage {index} {index === protagonist.stage && "(selected)"}
// </button>
// ))}
// </div>
<div style={{ display: "flex", flexWrap: "wrap" }}>
{characteristics.parameters.shells_upgrades.map((shell) => (
<button
key={shell.shell_id}
onClick={() => {
Tankopedia.mutate((draft) => {
draft.protagonist.shell = shell.shell_id;
});
}}
>
Shell {shell.shell_id}{" "}
{shell.shell_id === protagonist.shell && "(selected)"}
</button>
))}
</div>
// <br />
<br />
// <div style={{ display: "flex", flexWrap: "wrap" }}>
// {characteristics.parameters.shells_upgrades.map((shell) => (
// <button
// key={shell.shell_id}
// onClick={() => {
// Tankopedia.mutate((draft) => {
// draft.protagonist.shell = shell.shell_id;
// });
// }}
// >
// Shell {shell.shell_id}{" "}
// {shell.shell_id === protagonist.shell && "(selected)"}
// </button>
// ))}
// </div>
<div>
<span>speed (0 - {characteristics.characteristics.speed_forward})</span>
<input
type="range"
min={0}
max={characteristics.characteristics.speed_forward as number}
value={protagonist.speed}
onChange={(event) => {
Tankopedia.mutate((draft) => {
draft.protagonist.speed = event.target.valueAsNumber;
});
}}
/>
</div>
// <br />
<br />
// <div>
// <span>speed (0 - {characteristics.characteristics.speed_forward})</span>
// <input
// type="range"
// min={0}
// max={characteristics.characteristics.speed_forward as number}
// value={protagonist.speed}
// onChange={(event) => {
// Tankopedia.mutate((draft) => {
// draft.protagonist.speed = event.target.valueAsNumber;
// });
// }}
// />
// </div>
<div>
<span>isHullTraversing</span>
<input
type="checkbox"
value={`${protagonist.isHullTraversing}`}
onChange={(event) => {
Tankopedia.mutate((draft) => {
draft.protagonist.isHullTraversing = event.target.checked;
});
}}
/>
</div>
// <br />
<br />
// <div>
// <span>isHullTraversing</span>
// <input
// type="checkbox"
// value={`${protagonist.isHullTraversing}`}
// onChange={(event) => {
// Tankopedia.mutate((draft) => {
// draft.protagonist.isHullTraversing = event.target.checked;
// });
// }}
// />
// </div>
<div>
<span>isTurretTraversing</span>
<input
type="checkbox"
value={`${protagonist.isTurretTraversing}`}
onChange={(event) => {
Tankopedia.mutate((draft) => {
draft.protagonist.isTurretTraversing = event.target.checked;
});
}}
/>
</div>
// <br />
<br />
// <div>
// <span>isTurretTraversing</span>
// <input
// type="checkbox"
// value={`${protagonist.isTurretTraversing}`}
// onChange={(event) => {
// Tankopedia.mutate((draft) => {
// draft.protagonist.isTurretTraversing = event.target.checked;
// });
// }}
// />
// </div>
<div>
<span>isShooting</span>
<input
type="checkbox"
value={`${protagonist.isShooting}`}
onChange={(event) => {
Tankopedia.mutate((draft) => {
draft.protagonist.isShooting = event.target.checked;
});
}}
/>
</div>
// <br />
<br />
// <div>
// <span>isShooting</span>
// <input
// type="checkbox"
// value={`${protagonist.isShooting}`}
// onChange={(event) => {
// Tankopedia.mutate((draft) => {
// draft.protagonist.isShooting = event.target.checked;
// });
// }}
// />
// </div>
<div>
<span>isGunDamaged</span>
<input
type="checkbox"
value={`${protagonist.isGunDamaged}`}
onChange={(event) => {
Tankopedia.mutate((draft) => {
draft.protagonist.isGunDamaged = event.target.checked;
});
}}
/>
</div>
// <br />
<br />
// <div>
// <span>isGunDamaged</span>
// <input
// type="checkbox"
// value={`${protagonist.isGunDamaged}`}
// onChange={(event) => {
// Tankopedia.mutate((draft) => {
// draft.protagonist.isGunDamaged = event.target.checked;
// });
// }}
// />
// </div>
<div style={{ display: "flex", flexWrap: "wrap" }}>
{Object.values(TerrainHardness).map(
(hardness) =>
typeof hardness === "number" && (
<button
key={hardness}
onClick={() => {
Tankopedia.mutate((draft) => {
draft.protagonist.terrainHardness = hardness;
});
}}
>
{TerrainHardness[hardness]}{" "}
{hardness === protagonist.terrainHardness && "(selected)"}
</button>
),
)}
</div>
// <br />
<br />
// <div style={{ display: "flex", flexWrap: "wrap" }}>
// {Object.values(TerrainHardness).map(
// (hardness) =>
// typeof hardness === "number" && (
// <button
// key={hardness}
// onClick={() => {
// Tankopedia.mutate((draft) => {
// draft.protagonist.terrainHardness = hardness;
// });
// }}
// >
// {TerrainHardness[hardness]}{" "}
// {hardness === protagonist.terrainHardness && "(selected)"}
// </button>
// ),
// )}
// </div>
<div>
<span>isCaughtOnFire</span>
<input
type="checkbox"
value={`${protagonist.isCaughtOnFire}`}
onChange={(event) => {
Tankopedia.mutate((draft) => {
draft.protagonist.isCaughtOnFire = event.target.checked;
});
}}
/>
</div>
// <br />
<br />
// <div>
// <span>isCaughtOnFire</span>
// <input
// type="checkbox"
// value={`${protagonist.isCaughtOnFire}`}
// onChange={(event) => {
// Tankopedia.mutate((draft) => {
// draft.protagonist.isCaughtOnFire = event.target.checked;
// });
// }}
// />
// </div>
<div style={{ display: "flex", flexWrap: "wrap" }}>
{Object.values(TankopediaCompare).map(
(c) =>
typeof c === "number" && (
<button
key={c}
onClick={() => {
Tankopedia.mutate((draft) => {
draft.compare = c;
});
}}
>
{TankopediaCompare[c]} {c === compare && "(selected)"}
</button>
),
)}
</div>
// <br />
<br />
// <div style={{ display: "flex", flexWrap: "wrap" }}>
// {Object.values(TankopediaCompare).map(
// (c) =>
// typeof c === "number" && (
// <button
// key={c}
// onClick={() => {
// Tankopedia.mutate((draft) => {
// draft.compare = c;
// });
// }}
// >
// {TankopediaCompare[c]} {c === compare && "(selected)"}
// </button>
// ),
// )}
// </div>
{characteristicsOrder.map(({ group, order }) => (
<>
<h2 key={group}>{group}</h2>
// <br />
{order.map((config) =>
"toy" in config ? (
<TankopediaToy key={`toy-${config.toy}`} toy={config.toy} />
) : (
<TankopediaCharacteristic
key={`characteristic-${config.name}`}
characteristic={characteristics.characteristics[config.name]}
others={[]}
config={config}
/>
),
)}
</>
))}
</>
);
// {characteristicsOrder.map(({ group, order }) => (
// <>
// <h2 key={group}>{group}</h2>
// {order.map((config) =>
// "toy" in config ? (
// <TankopediaToy key={`toy-${config.toy}`} toy={config.toy} />
// ) : (
// <TankopediaCharacteristic
// key={`characteristic-${config.name}`}
// characteristic={characteristics.characteristics[config.name]}
// others={[]}
// config={config}
// />
// ),
// )}
// </>
// ))}
// </>
// );
}