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 { StandardSinglePrice } from "@protos/game/proto/legacy/blitz_static_standard_single_price";
import { PenetrationGroup } from "@protos/game/proto/legacy/blitz_static_tank_penetration_group"; import { PenetrationGroup } from "@protos/game/proto/legacy/blitz_static_tank_penetration_group";
import { import {
@@ -7,13 +7,14 @@ import {
ShellUpgrade, ShellUpgrade,
ShellUpgradeSingleChange, ShellUpgradeSingleChange,
StageParameters, StageParameters,
TankAttributeChange,
TankAttributeChange_AttributeName, TankAttributeChange_AttributeName,
TankAttributeChange_Modifier, TankAttributeChange_Modifier,
VisualChanges, VisualChanges,
} from "@protos/game/proto/legacy/blitz_static_tank_upgrade_single_stage"; } from "@protos/game/proto/legacy/blitz_static_tank_upgrade_single_stage";
function patch(stage0: StageParameters, stage1: StageParameters) { 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"); 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( export function aggregateParameters(
base: StageParameters, tank: TankCatalogComponent,
stages: StageParameters[], upgrades: Record<string, number>,
) { ) {
const stage0 = StageParameters.create({ ...base, stage_number: 0 }); const stage0 = StageParameters.create({});
/** for (const line of tank.upgrade_lines) {
* Bug in Reforged: base stats are stage 1, so is the first upgrade. Manually const stage = upgrades[line.name];
* 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.
*/
if (base.stage_number === 0 && import.meta.env.DEV) { stage0.number = 0;
throw new Error(
"Base stats are now stage 0. In-game bug fixed, please remove hack.", for (let i = 0; i < stage; i++) {
); patch(stage0, line.stages[i]);
}
} }
stages.forEach((stage) => patch(stage0, stage));
return stage0; return stage0;
} }
@@ -3,7 +3,7 @@ import {
ShellUpgradeSingleChange_AttributeName, ShellUpgradeSingleChange_AttributeName,
TankAttributeChange_AttributeName, TankAttributeChange_AttributeName,
} from "@protos/game/proto/legacy/blitz_static_tank_upgrade_single_stage"; } from "@protos/game/proto/legacy/blitz_static_tank_upgrade_single_stage";
import { aggregateStageParameters } from "./aggregateStageParameters"; import { aggregateStageParameters } from "./aggregateParameters";
import { import {
characteristics, characteristics,
type CharacteristicName, type CharacteristicName,
@@ -1,15 +1,15 @@
import { times } from "lodash-es"; import { Grade } from "@blitzkit/closed/protos/game/proto/legacy/blitz_static_standard_grades_enum";
import { Suspense, useMemo } from "react"; import {
import { TankopediaCharacteristic } from "../../../../components/TankopediaCharacteristic"; StageParameters_StageType,
import { TankopediaToy } from "../../../../components/TankopediaToy"; 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 { api } from "../../../../core/api/dynamic";
import { characteristicsOrder } from "../../../../core/tankopedia/characteristicsOrder"; import { aggregateParameters } from "../../../../core/tankopedia/aggregateParameters";
import { computeCharacteristics } from "../../../../core/tankopedia/computeCharacteristics";
import { TerrainHardness } from "../../../../core/tankopedia/tankState";
import { useAwait } from "../../../../hooks/useAwait"; import { useAwait } from "../../../../hooks/useAwait";
import { useGameStrings } from "../../../../hooks/useGameStrings"; import { useGameStrings } from "../../../../hooks/useGameStrings";
import { LocaleProvider } from "../../../../hooks/useLocale"; import { LocaleProvider } from "../../../../hooks/useLocale";
import { Tankopedia, TankopediaCompare } from "../../../../stores/tankopedia"; import { Tankopedia } from "../../../../stores/tankopedia";
interface PageProps { interface PageProps {
id: string; id: string;
@@ -43,21 +43,44 @@ function Content() {
`tank-${protagonist.id}`, `tank-${protagonist.id}`,
); );
const parameters = aggregateParameters(
protagonistTank.tank!,
protagonist.upgrades,
);
return ( return (
<> <>
<h1>{protagonist.id}</h1> <h1>{protagonist.id}</h1>
<br /> <br />
<h2>upgrades</h2>
{protagonistTank.tank!.upgrade_lines.map((line) => { {protagonistTank.tank!.upgrade_lines.map((line) => {
return ( return (
<> <>
<h2> <h3>
{line.name} ({protagonist.upgrades[line.name]}) {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) => { {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 ( return (
<button <button
key={stage.tech_name} 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]} {tankEntityGameStrings[stage.display_name]}
</button> </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 compare = Tankopedia.use((state) => state.compare);
const characteristics = useMemo( // const characteristics = useMemo(
() => computeCharacteristics(id, tank, protagonist), // () => computeCharacteristics(id, tank, protagonist),
[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 otherCharacteristics = useMemo(
// // () =>
// // tankList.list
// // .filter((tank) => tank.id !== id)
// // .map(({ id }) => {
// // const state = { ...protagonist };
// // const tank = otherTanks.tanks[id];
return ( // // return computeCharacteristics(
<> // // id,
<h1>{id}</h1> // // tank.tank!,
// // tank.compensation!,
// // state
// // );
// // }),
// // [protagonist]
// // );
<div style={{ display: "flex", flexWrap: "wrap", gap: "1rem" }}> // return (
{tank.tank?.feature_screens?.tags.map((tag) => ( // <>
<span>{tag.value}</span> // <h1>{id}</h1>
))}
</div>
<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" }}> // <br />
{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 /> // <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" }}> // <br />
{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 /> // <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> // <br />
<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 /> // <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> // <br />
<span>isHullTraversing</span>
<input
type="checkbox"
value={`${protagonist.isHullTraversing}`}
onChange={(event) => {
Tankopedia.mutate((draft) => {
draft.protagonist.isHullTraversing = event.target.checked;
});
}}
/>
</div>
<br /> // <div>
// <span>isHullTraversing</span>
// <input
// type="checkbox"
// value={`${protagonist.isHullTraversing}`}
// onChange={(event) => {
// Tankopedia.mutate((draft) => {
// draft.protagonist.isHullTraversing = event.target.checked;
// });
// }}
// />
// </div>
<div> // <br />
<span>isTurretTraversing</span>
<input
type="checkbox"
value={`${protagonist.isTurretTraversing}`}
onChange={(event) => {
Tankopedia.mutate((draft) => {
draft.protagonist.isTurretTraversing = event.target.checked;
});
}}
/>
</div>
<br /> // <div>
// <span>isTurretTraversing</span>
// <input
// type="checkbox"
// value={`${protagonist.isTurretTraversing}`}
// onChange={(event) => {
// Tankopedia.mutate((draft) => {
// draft.protagonist.isTurretTraversing = event.target.checked;
// });
// }}
// />
// </div>
<div> // <br />
<span>isShooting</span>
<input
type="checkbox"
value={`${protagonist.isShooting}`}
onChange={(event) => {
Tankopedia.mutate((draft) => {
draft.protagonist.isShooting = event.target.checked;
});
}}
/>
</div>
<br /> // <div>
// <span>isShooting</span>
// <input
// type="checkbox"
// value={`${protagonist.isShooting}`}
// onChange={(event) => {
// Tankopedia.mutate((draft) => {
// draft.protagonist.isShooting = event.target.checked;
// });
// }}
// />
// </div>
<div> // <br />
<span>isGunDamaged</span>
<input
type="checkbox"
value={`${protagonist.isGunDamaged}`}
onChange={(event) => {
Tankopedia.mutate((draft) => {
draft.protagonist.isGunDamaged = event.target.checked;
});
}}
/>
</div>
<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" }}> // <br />
{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 /> // <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> // <br />
<span>isCaughtOnFire</span>
<input
type="checkbox"
value={`${protagonist.isCaughtOnFire}`}
onChange={(event) => {
Tankopedia.mutate((draft) => {
draft.protagonist.isCaughtOnFire = event.target.checked;
});
}}
/>
</div>
<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" }}> // <br />
{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 /> // <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 }) => ( // <br />
<>
<h2 key={group}>{group}</h2>
{order.map((config) => // {characteristicsOrder.map(({ group, order }) => (
"toy" in config ? ( // <>
<TankopediaToy key={`toy-${config.toy}`} toy={config.toy} /> // <h2 key={group}>{group}</h2>
) : (
<TankopediaCharacteristic // {order.map((config) =>
key={`characteristic-${config.name}`} // "toy" in config ? (
characteristic={characteristics.characteristics[config.name]} // <TankopediaToy key={`toy-${config.toy}`} toy={config.toy} />
others={[]} // ) : (
config={config} // <TankopediaCharacteristic
/> // key={`characteristic-${config.name}`}
), // characteristic={characteristics.characteristics[config.name]}
)} // others={[]}
</> // config={config}
))} // />
</> // ),
); // )}
// </>
// ))}
// </>
// );
} }