Updating mechanics for OS protocols
This commit is contained in:
+191
-5
@@ -45,7 +45,8 @@ const isRoll = (value) =>
|
||||
isFiniteNumber(value.value);
|
||||
const isRollPair = (value) =>
|
||||
Array.isArray(value) && value.length === 2 && value.every(isRoll);
|
||||
const isGearSelection = (value) =>
|
||||
const isTalentTier = (value) => value === 1 || value === 2;
|
||||
const isLegacyGearSelection = (value) =>
|
||||
hasExactKeys(value, [
|
||||
'itemId',
|
||||
'level',
|
||||
@@ -68,7 +69,7 @@ const isGearSelection = (value) =>
|
||||
isRollPair(value.rolls) &&
|
||||
Array.isArray(value.augmentSlots) &&
|
||||
value.augmentSlots.every(isFiniteNumber);
|
||||
const isWeaponSelection = (value) =>
|
||||
const isLegacyWeaponSelection = (value) =>
|
||||
hasExactKeys(value, [
|
||||
'itemId',
|
||||
'level',
|
||||
@@ -88,6 +89,57 @@ const isWeaponSelection = (value) =>
|
||||
(key) => isFiniteNumber(value[key]),
|
||||
) &&
|
||||
isRollPair(value.rolls);
|
||||
const isGearSelection = (value) =>
|
||||
hasExactKeys(value, [
|
||||
'itemId',
|
||||
'level',
|
||||
'brandId',
|
||||
'talentId',
|
||||
'talentTier',
|
||||
'health',
|
||||
'armor',
|
||||
'firepower',
|
||||
'toughness',
|
||||
'engineering',
|
||||
'rolls',
|
||||
'augmentSlots',
|
||||
]) &&
|
||||
['itemId', 'brandId', 'talentId'].every(
|
||||
(key) => typeof value[key] === 'string',
|
||||
) &&
|
||||
isTalentTier(value.talentTier) &&
|
||||
['level', 'health', 'armor', 'firepower', 'toughness', 'engineering'].every(
|
||||
(key) => isFiniteNumber(value[key]),
|
||||
) &&
|
||||
isRollPair(value.rolls) &&
|
||||
Array.isArray(value.augmentSlots) &&
|
||||
value.augmentSlots.every(isFiniteNumber);
|
||||
const isWeaponSelection = (value) =>
|
||||
hasExactKeys(value, [
|
||||
'itemId',
|
||||
'level',
|
||||
'tier',
|
||||
'damage',
|
||||
'firepower',
|
||||
'toughness',
|
||||
'engineering',
|
||||
'talent1Id',
|
||||
'talent1Tier',
|
||||
'talent2Id',
|
||||
'talent2Tier',
|
||||
'exoticTalentTier',
|
||||
'rolls',
|
||||
]) &&
|
||||
['itemId', 'talent1Id', 'talent2Id'].every(
|
||||
(key) => typeof value[key] === 'string',
|
||||
) &&
|
||||
isTalentTier(value.talent1Tier) &&
|
||||
isTalentTier(value.talent2Tier) &&
|
||||
isTalentTier(value.exoticTalentTier) &&
|
||||
['level', 'tier', 'damage', 'firepower', 'toughness', 'engineering'].every(
|
||||
(key) => isFiniteNumber(value[key]),
|
||||
) &&
|
||||
isRollPair(value.rolls);
|
||||
const isChipSelection = (value) =>
|
||||
hasExactKeys(value, ['setId', 'quality', 'primary', 'secondary']) &&
|
||||
typeof value.setId === 'string' &&
|
||||
@@ -190,7 +242,7 @@ const isCurrentLoadoutSnapshot = (raw) => {
|
||||
'skillActivations',
|
||||
'specialization',
|
||||
]) &&
|
||||
value.version === 5 &&
|
||||
value.version === 6 &&
|
||||
typeof value.buildName === 'string' &&
|
||||
['maximum', 'custom'].includes(value.calculationMode) &&
|
||||
hasExactKeys(value.gear, gearSlots) &&
|
||||
@@ -212,6 +264,49 @@ const isCurrentLoadoutSnapshot = (raw) => {
|
||||
);
|
||||
};
|
||||
|
||||
const isLegacyLoadoutSnapshot = (raw) => {
|
||||
let value;
|
||||
try {
|
||||
value = typeof raw === 'string' ? JSON.parse(raw) : raw;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
hasExactKeys(value, [
|
||||
'version',
|
||||
'buildName',
|
||||
'gear',
|
||||
'weapons',
|
||||
'chips',
|
||||
'protocol',
|
||||
'conditionalEffects',
|
||||
'combatProfile',
|
||||
'calculationMode',
|
||||
'skillActivations',
|
||||
'specialization',
|
||||
]) &&
|
||||
value.version === 5 &&
|
||||
typeof value.buildName === 'string' &&
|
||||
['maximum', 'custom'].includes(value.calculationMode) &&
|
||||
hasExactKeys(value.gear, gearSlots) &&
|
||||
gearSlots.every((slot) => isLegacyGearSelection(value.gear[slot])) &&
|
||||
Array.isArray(value.weapons) &&
|
||||
value.weapons.length === 2 &&
|
||||
value.weapons.every(isLegacyWeaponSelection) &&
|
||||
Array.isArray(value.chips) &&
|
||||
value.chips.length === 9 &&
|
||||
value.chips.every(isChipSelection) &&
|
||||
isProtocolSelection(value.protocol) &&
|
||||
isEffectStates(value.conditionalEffects) &&
|
||||
isCombatProfile(value.combatProfile) &&
|
||||
isSkillActivations(value.skillActivations) &&
|
||||
hasExactKeys(value.specialization, ['focusId', 'nodeRanks']) &&
|
||||
typeof value.specialization.focusId === 'string' &&
|
||||
isRecord(value.specialization.nodeRanks) &&
|
||||
Object.values(value.specialization.nodeRanks).every(isFiniteNumber)
|
||||
);
|
||||
};
|
||||
|
||||
const databasePath = resolve(
|
||||
process.env.TDR_DATABASE_PATH || './data/shd-build.db',
|
||||
);
|
||||
@@ -3223,6 +3318,41 @@ if (
|
||||
}
|
||||
}
|
||||
|
||||
const [loadoutGearRows, loadoutWeaponRows, loadoutTalentTierRows] =
|
||||
await Promise.all([
|
||||
(await database.prepare('SELECT id,tier FROM gear')).all(),
|
||||
(await database.prepare('SELECT id,exotic_talent_id FROM weapons')).all(),
|
||||
(await database.prepare('SELECT talent_id,tier FROM talent_tiers')).all(),
|
||||
]);
|
||||
const migrateLegacyLoadoutSnapshot = (raw) => {
|
||||
if (!isLegacyLoadoutSnapshot(raw)) return null;
|
||||
const value =
|
||||
typeof raw === 'string' ? JSON.parse(raw) : structuredClone(raw);
|
||||
for (const slot of gearSlots) {
|
||||
const item = loadoutGearRows.find(
|
||||
(entry) => String(entry.id) === String(value.gear[slot].itemId),
|
||||
);
|
||||
value.gear[slot].talentTier = Number(item?.tier) >= 2 ? 2 : 1;
|
||||
}
|
||||
for (const selection of value.weapons) {
|
||||
const inheritedTier = Number(selection.tier) >= 2 ? 2 : 1;
|
||||
selection.talent1Tier = inheritedTier;
|
||||
selection.talent2Tier = inheritedTier;
|
||||
const weapon = loadoutWeaponRows.find(
|
||||
(entry) => String(entry.id) === String(selection.itemId),
|
||||
);
|
||||
const exoticTalentId = String(weapon?.exotic_talent_id ?? '');
|
||||
const hasExoticTierTwo = loadoutTalentTierRows.some(
|
||||
(entry) =>
|
||||
String(entry.talent_id) === exoticTalentId && entry.tier === 'II',
|
||||
);
|
||||
selection.exoticTalentTier =
|
||||
inheritedTier === 2 && hasExoticTierTwo ? 2 : 1;
|
||||
}
|
||||
value.version = 6;
|
||||
return isCurrentLoadoutSnapshot(value) ? JSON.stringify(value) : null;
|
||||
};
|
||||
|
||||
const buildColumns = await (
|
||||
await database.prepare("PRAGMA table_info('builds')")
|
||||
).all();
|
||||
@@ -3237,9 +3367,34 @@ if (buildColumns.length) {
|
||||
await database.exec(
|
||||
"UPDATE builds SET visibility='private' WHERE visibility='draft'",
|
||||
);
|
||||
const builds = await (
|
||||
let builds = await (
|
||||
await database.prepare('SELECT id,loadout_json FROM builds')
|
||||
).all();
|
||||
const migratedBuilds = builds
|
||||
.map((build) => ({
|
||||
id: String(build.id),
|
||||
loadout: migrateLegacyLoadoutSnapshot(build.loadout_json),
|
||||
}))
|
||||
.filter((build) => build.loadout);
|
||||
if (migratedBuilds.length) {
|
||||
console.log(
|
||||
`Migrating ${migratedBuilds.length} build loadout snapshot(s) to version 6 Talent tiers.`,
|
||||
);
|
||||
await database.exec('BEGIN');
|
||||
try {
|
||||
for (const build of migratedBuilds)
|
||||
await (
|
||||
await database.prepare('UPDATE builds SET loadout_json=? WHERE id=?')
|
||||
).run(build.loadout, build.id);
|
||||
await database.exec('COMMIT');
|
||||
} catch (error) {
|
||||
await database.exec('ROLLBACK');
|
||||
throw error;
|
||||
}
|
||||
builds = await (
|
||||
await database.prepare('SELECT id,loadout_json FROM builds')
|
||||
).all();
|
||||
}
|
||||
const obsoleteBuildIds = builds
|
||||
.filter((build) => !isCurrentLoadoutSnapshot(build.loadout_json))
|
||||
.map((build) => String(build.id));
|
||||
@@ -3629,11 +3784,42 @@ if (
|
||||
);
|
||||
}
|
||||
|
||||
const calibrationCases = await (
|
||||
let calibrationCases = await (
|
||||
await database.prepare(
|
||||
'SELECT id,loadout_snapshot_json FROM calculation_calibration_cases',
|
||||
)
|
||||
).all();
|
||||
const migratedCalibrationCases = calibrationCases
|
||||
.map((calibrationCase) => ({
|
||||
id: String(calibrationCase.id),
|
||||
loadout: migrateLegacyLoadoutSnapshot(
|
||||
calibrationCase.loadout_snapshot_json,
|
||||
),
|
||||
}))
|
||||
.filter((calibrationCase) => calibrationCase.loadout);
|
||||
if (migratedCalibrationCases.length) {
|
||||
console.log(
|
||||
`Migrating ${migratedCalibrationCases.length} calibration snapshot(s) to version 6 Talent tiers.`,
|
||||
);
|
||||
await database.exec('BEGIN');
|
||||
try {
|
||||
for (const calibrationCase of migratedCalibrationCases)
|
||||
await (
|
||||
await database.prepare(
|
||||
'UPDATE calculation_calibration_cases SET loadout_snapshot_json=? WHERE id=?',
|
||||
)
|
||||
).run(calibrationCase.loadout, calibrationCase.id);
|
||||
await database.exec('COMMIT');
|
||||
} catch (error) {
|
||||
await database.exec('ROLLBACK');
|
||||
throw error;
|
||||
}
|
||||
calibrationCases = await (
|
||||
await database.prepare(
|
||||
'SELECT id,loadout_snapshot_json FROM calculation_calibration_cases',
|
||||
)
|
||||
).all();
|
||||
}
|
||||
const obsoleteCalibrationIds = calibrationCases
|
||||
.filter(
|
||||
(calibrationCase) =>
|
||||
|
||||
Reference in New Issue
Block a user