Compare commits

...
6 Commits
Author SHA1 Message Date
TrèsAbhi 48fbf59036 feat: override common data 2025-12-18 13:20:35 -06:00
TrèsAbhi 0e0f4fbe9f fix: find att0 correctly 2025-12-18 13:16:20 -06:00
TrèsAbhi 6ba6e10108 feat: rename 2025-12-18 13:08:42 -06:00
TrèsAbhi cf8fa4ffb4 feat: remaining stagers 2025-12-18 12:59:12 -06:00
TrèsAbhi 2b0180dce2 Merge branch 'main' into ue 2025-12-18 12:45:10 -06:00
TrèsAbhi de8d3396b1 fix: use normalized dvpl path 2025-12-16 12:35:56 -06:00
3 changed files with 116 additions and 73 deletions

No files matched your search

+1 -1
View File
@@ -21,7 +21,7 @@ export abstract class AbstractVFS {
const normalized = normalize(path);
if (this.has(normalized)) return normalized;
const dvplPath = `${path}.dvpl`;
const dvplPath = `${normalized}.dvpl`;
if (this.has(dvplPath)) return dvplPath;
return null;
@@ -2,125 +2,132 @@ import { StandardSinglePrice } from "@protos/blitz_static_standard_single_price"
import { PenetrationGroup } from "@protos/blitz_static_tank_penetration_group";
import {
PenetrationGroupUpgrade,
PitchLimit,
ShellUpgrade,
ShellUpgrageSingleChange,
StageParameters,
TankAttributeChange,
TankAttributeChange_AttributeName,
TankAttributeChange_Modifier,
VisualChanges,
} from "@protos/blitz_static_tank_upgrade_single_stage";
import { times } from "lodash-es";
function patch(change: StageParameters, base: StageParameters) {
if (change.stage_number !== ++base.stage_number) {
function patch(stage0: StageParameters, stage1: StageParameters) {
if (stage1.stage_number !== ++stage0.stage_number) {
throw new Error("Change stage number must be 1 greater than base");
}
for (const changedAttribute of change.attributes) {
let newValue: number;
for (const attribute1 of stage1.attributes) {
let value1: number;
switch (changedAttribute.modifier) {
switch (attribute1.modifier) {
case TankAttributeChange_Modifier.MODIFIER_OVERRIDE:
newValue = changedAttribute.value;
value1 = attribute1.value;
break;
case TankAttributeChange_Modifier.MODIFIER_MULTIPLY:
case TankAttributeChange_Modifier.MODIFIER_ADD:
if (!(changedAttribute.attribute_name in base)) {
const attribute0 = stage0.attributes.find(
(attribute0) =>
attribute0.attribute_name === attribute1.attribute_name
);
if (!attribute0) {
throw new Error(
`Missing attribute ${
TankAttributeChange_AttributeName[changedAttribute.attribute_name]
} to modify`
TankAttributeChange_AttributeName[attribute1.attribute_name]
} (${attribute1.attribute_name}) to modify`
);
}
const oldValue =
base.attributes[changedAttribute.attribute_name]!.value;
case TankAttributeChange_Modifier.MODIFIER_MULTIPLY: {
newValue = oldValue! * changedAttribute.value;
value1 = attribute0!.value! * attribute1.value;
break;
}
case TankAttributeChange_Modifier.MODIFIER_ADD: {
newValue = oldValue! + changedAttribute.value;
value1 = attribute0!.value! + attribute1.value;
break;
}
default:
throw new Error(
`Unhandled modified ${
TankAttributeChange_Modifier[changedAttribute.modifier]
TankAttributeChange_Modifier[attribute1.modifier]
}`
);
}
base.attributes[changedAttribute.attribute_name] = {
const attribute0 = TankAttributeChange.create({
modifier: TankAttributeChange_Modifier.MODIFIER_OVERRIDE,
attribute_name: changedAttribute.attribute_name,
value: newValue,
};
attribute_name: attribute1.attribute_name,
value: value1,
});
const index0 = stage0.attributes.findIndex(
(attribute) => attribute.attribute_name === attribute1.attribute_name
);
if (index0 === -1) {
stage0.attributes.push(attribute0);
} else {
stage0.attributes[index0] = attribute0;
}
}
for (const changedPenetrationGroupUpgrades of change.penetration_groups_upgrades) {
if (changedPenetrationGroupUpgrades.primary_armor.length > 0) {
for (const penetrationGroupUpgrade1 of stage1.penetration_groups_upgrades) {
if (penetrationGroupUpgrade1.primary_armor.length > 0) {
throw new Error("Primary armor is not empty; implement this");
}
let basePenetrationGroupUpgrades = base.penetration_groups_upgrades.find(
(group) => group.tank_part === changedPenetrationGroupUpgrades.tank_part
let penetrationGroupUpgrade0 = stage0.penetration_groups_upgrades.find(
(group) => group.tank_part === penetrationGroupUpgrade1.tank_part
);
if (!basePenetrationGroupUpgrades) {
basePenetrationGroupUpgrades = PenetrationGroupUpgrade.create({
tank_part: changedPenetrationGroupUpgrades.tank_part,
if (!penetrationGroupUpgrade0) {
penetrationGroupUpgrade0 = PenetrationGroupUpgrade.create({
tank_part: penetrationGroupUpgrade1.tank_part,
});
base.penetration_groups_upgrades.push(basePenetrationGroupUpgrades);
stage0.penetration_groups_upgrades.push(penetrationGroupUpgrade0);
}
for (const changedPenetrationGroup of changedPenetrationGroupUpgrades.penetration_groups) {
const basePenetrationGroup =
basePenetrationGroupUpgrades.penetration_groups.find(
(baseGroup) =>
baseGroup.group_name === changedPenetrationGroup.group_name
for (const penetrationGroup1 of penetrationGroupUpgrade1.penetration_groups) {
const penetrationGroup0 =
penetrationGroupUpgrade0.penetration_groups.find(
(baseGroup) => baseGroup.group_name === penetrationGroup1.group_name
);
if (basePenetrationGroup) {
if (
basePenetrationGroup.common_data !==
changedPenetrationGroup.common_data
) {
throw new Error("Common data is not the same");
}
basePenetrationGroup.armor = changedPenetrationGroup.armor;
if (penetrationGroup0) {
penetrationGroup0.common_data = penetrationGroup1.common_data;
penetrationGroup0.armor = penetrationGroup1.armor;
} else {
basePenetrationGroupUpgrades.penetration_groups.push(
PenetrationGroup.create(changedPenetrationGroup)
penetrationGroupUpgrade0.penetration_groups.push(
PenetrationGroup.create(penetrationGroup1)
);
}
}
}
if (change.modules_upgrades.length > 0) {
if (stage1.modules_upgrades.length > 0) {
throw new Error("Modules upgrades not implemented");
}
for (const changedShellUpgrades of change.shells_upgrades) {
let baseShellUpgrades = base.shells_upgrades.find(
(baseShell) => baseShell.shell_id === changedShellUpgrades.shell_id
for (const shellUpgrades1 of stage1.shells_upgrades) {
let shellUpgrades0 = stage0.shells_upgrades.find(
(baseShell) => baseShell.shell_id === shellUpgrades1.shell_id
);
if (!baseShellUpgrades) {
baseShellUpgrades = ShellUpgrade.create({
shell_id: changedShellUpgrades.shell_id,
if (!shellUpgrades0) {
shellUpgrades0 = ShellUpgrade.create({
shell_id: shellUpgrades1.shell_id,
});
}
baseShellUpgrades.shell_type = changedShellUpgrades.shell_type;
shellUpgrades0.shell_type = shellUpgrades1.shell_type;
for (const changedShellUpgrade of changedShellUpgrades.changes) {
const baseShellUpgrade = baseShellUpgrades.changes.find(
for (const changedShellUpgrade of shellUpgrades1.changes) {
const baseShellUpgrade = shellUpgrades0.changes.find(
(baseShellUpgrade) =>
baseShellUpgrade.attribute_name === changedShellUpgrade.attribute_name
);
@@ -128,16 +135,52 @@ function patch(change: StageParameters, base: StageParameters) {
if (baseShellUpgrade) {
baseShellUpgrade.value = changedShellUpgrade.value;
} else {
baseShellUpgrades.changes.push(
shellUpgrades0.changes.push(
ShellUpgrageSingleChange.create(changedShellUpgrade)
);
}
}
baseShellUpgrades.silver_price =
changedShellUpgrades.silver_price === undefined
shellUpgrades0.silver_price =
shellUpgrades1.silver_price === undefined
? undefined
: StandardSinglePrice.create(changedShellUpgrades.silver_price);
: StandardSinglePrice.create(shellUpgrades1.silver_price);
}
if (stage1.pump_reload_times.length > 0) {
throw new Error("Pump reload times not implemented");
}
if (stage1.pitch_limits_up.length > 0 && stage0.pitch_limits_up.length > 0) {
throw new Error("Pitch limits not implemented");
}
for (const pitchLimitUp1 of stage1.pitch_limits_up) {
stage0.pitch_limits_up.push(PitchLimit.create(pitchLimitUp1));
}
if (
stage1.pitch_limits_down.length > 0 &&
stage0.pitch_limits_down.length > 0
) {
throw new Error("Pitch limits not implemented");
}
for (const pitchLimitDown1 of stage1.pitch_limits_down) {
stage0.pitch_limits_down.push(PitchLimit.create(pitchLimitDown1));
}
for (const visualChange1 of stage1.visual_changes) {
const visualChange0 = stage0.visual_changes.find(
(baseVisualChange) =>
baseVisualChange.tank_part === visualChange1.tank_part
);
if (visualChange0) {
visualChange0.name = visualChange1.name;
} else {
stage0.visual_changes.push(VisualChanges.create(visualChange1));
}
}
}
@@ -146,9 +189,9 @@ export function aggregateStages(
stages: StageParameters[],
stage: number
) {
const attributes = StageParameters.create();
const stage0 = StageParameters.create();
patch(base, attributes);
patch(stage0, base);
/**
* Bug in Reforged: base stats are stage 1, so is the first upgrade. Manually
@@ -157,15 +200,15 @@ export function aggregateStages(
* stage number to -1.
*/
if (attributes.stage_number === 0 && import.meta.env.DEV) {
if (stage0.stage_number === 0 && import.meta.env.DEV) {
throw new Error(
"Base stats are now stage 0. In-game bug fixed, please remove hack."
);
}
attributes.stage_number = 0;
stage0.stage_number = 0;
times(stage, (index) => patch(stages[index], attributes));
times(stage, (index) => patch(stage0, stages[index]));
return attributes;
return stage0;
}
@@ -17,33 +17,33 @@ function Content({ id }: { id: string }) {
const tank = useAwait(() => api.tank(id), `tank-${id}`);
const stage = Tankopedia.use((state) => state.stage);
const aggregatedStages = useMemo(
const parameters = useMemo(
() => aggregateStages(tank.base_stats!, tank.upgrade_stages, stage),
[stage]
);
return (
<>
<pre children={JSON.stringify(tank, null, 2)} />
<h1>{tank.name?.value}</h1>
<h2>Characteristics</h2>
<div style={{ display: "flex" }}>
<div style={{ display: "flex", flexWrap: "wrap" }}>
{times(tank.upgrade_stages.length, (index) => (
<button
key={index}
onClick={() => {
Tankopedia.mutate((draft) => {
draft.stage = index;
});
}}
>
Stage {index + 1} {index === stage && "(selected)"}
Stage {index} {index === stage && "(selected)"}
</button>
))}
</div>
<pre children={JSON.stringify(parameters, null, 2)} />
<h1>{tank.name?.value}</h1>
<h2>Characteristics</h2>
{/* {_characteristicsOrder.map((group) => (
<>
<h2>{CharacteristicsGroup[group.group]}</h2>