feat(isac): add specialization data management

This commit is contained in:
JB
2026-07-26 16:20:04 -04:00
parent ed31748102
commit 6722665f5c
25 changed files with 3369 additions and 25 deletions
+57 -1
View File
@@ -183,6 +183,40 @@ if (typeof weaponModProposalDefinition?.sql === 'string' && !weaponModProposalDe
await database.exec('CREATE UNIQUE INDEX IF NOT EXISTS data_change_proposals_draft_entity_idx ON data_change_proposals(draft_entity_id) WHERE draft_entity_id IS NOT NULL');
}
const specializationProposalDefinition = await (await database.prepare(
"SELECT sql FROM sqlite_master WHERE type='table' AND name='data_change_proposals'"
)).get();
if (typeof specializationProposalDefinition?.sql === 'string' && !specializationProposalDefinition.sql.includes("'specializations'")) {
console.log('Adding Specializations to moderated data modules.');
await database.exec('PRAGMA foreign_keys=OFF');
await database.exec(`CREATE TABLE data_change_proposals_specializations (
id TEXT PRIMARY KEY,
module TEXT NOT NULL CHECK(module IN ('configuration','attributes','talents','brands','gear','weapons','weaponmods','gearmods','protocols','skillchips','specializations')),
action TEXT NOT NULL,
entity_id TEXT,
draft_entity_id TEXT,
applied_entity_id TEXT,
summary TEXT NOT NULL,
payload_json TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending','approved','rejected','failed')),
proposed_by TEXT NOT NULL REFERENCES users(id),
reviewed_by TEXT REFERENCES users(id),
review_note TEXT,
created_at TEXT NOT NULL,
reviewed_at TEXT,
applied_at TEXT
)`);
await database.exec(`INSERT INTO data_change_proposals_specializations
SELECT id,module,action,entity_id,draft_entity_id,applied_entity_id,summary,payload_json,status,proposed_by,reviewed_by,review_note,created_at,reviewed_at,applied_at
FROM data_change_proposals`);
await database.exec('DROP TABLE data_change_proposals');
await database.exec('ALTER TABLE data_change_proposals_specializations RENAME TO data_change_proposals');
await database.exec('PRAGMA foreign_keys=ON');
await database.exec('CREATE INDEX IF NOT EXISTS data_change_proposals_status_created_idx ON data_change_proposals(status,created_at DESC)');
await database.exec('CREATE INDEX IF NOT EXISTS data_change_proposals_module_status_idx ON data_change_proposals(module,status)');
await database.exec('CREATE UNIQUE INDEX IF NOT EXISTS data_change_proposals_draft_entity_idx ON data_change_proposals(draft_entity_id) WHERE draft_entity_id IS NOT NULL');
}
const iconDefinition = await (await database.prepare(
"SELECT sql FROM sqlite_master WHERE type='table' AND name='game_data_icons'"
)).get();
@@ -201,6 +235,25 @@ if (typeof iconDefinition?.sql === 'string' && !iconDefinition.sql.includes("'sk
await database.exec('ALTER TABLE game_data_icons_expanded RENAME TO game_data_icons');
}
const specializationIconDefinition = await (await database.prepare(
"SELECT sql FROM sqlite_master WHERE type='table' AND name='game_data_icons'"
)).get();
if (typeof specializationIconDefinition?.sql === 'string' && !specializationIconDefinition.sql.includes("'specialization_node'")) {
console.log('Adding Specialization icon storage support.');
await database.exec(`CREATE TABLE game_data_icons_specializations (
entity_type TEXT NOT NULL CHECK(entity_type IN ('talent', 'brand_set', 'os_protocol', 'skill_chip', 'specialization', 'specialization_ability', 'specialization_node')),
entity_id TEXT NOT NULL,
storage_key TEXT NOT NULL UNIQUE,
byte_size INTEGER NOT NULL CHECK(byte_size > 0),
updated_at TEXT NOT NULL,
PRIMARY KEY(entity_type, entity_id)
)`);
await database.exec(`INSERT INTO game_data_icons_specializations
SELECT entity_type,entity_id,storage_key,byte_size,updated_at FROM game_data_icons`);
await database.exec('DROP TABLE game_data_icons');
await database.exec('ALTER TABLE game_data_icons_specializations RENAME TO game_data_icons');
}
const gearAugmentColumns = await (await database.prepare("PRAGMA table_info('gear_tactical_augments')")).all();
if (gearAugmentColumns.length && !gearAugmentColumns.some((column) => column.name === 'effect_type')) {
console.log('Separating custom Gear Tactical Augments from fixed base-item percentage upgrades.');
@@ -290,11 +343,14 @@ if (!weaponRangeColumns.some((column) => column.name === 'reference_level')) {
const attributeColumns = await (await database.prepare("PRAGMA table_info('stat_attributes')")).all();
for (const [name, definition] of [
['description', 'TEXT'],
['gear_reference_level', 'INTEGER NOT NULL DEFAULT 50 CHECK(gear_reference_level BETWEEN 1 AND 50)'],
['weapon_reference_level', 'INTEGER NOT NULL DEFAULT 50 CHECK(weapon_reference_level BETWEEN 1 AND 50)']
]) {
if (!attributeColumns.some((column) => column.name === name)) {
console.log(`Recording the reference level for Attribute source ranges: ${name}.`);
console.log(name === 'description'
? 'Adding optional Attribute descriptions.'
: `Recording the reference level for Attribute source ranges: ${name}.`);
await database.exec(`ALTER TABLE stat_attributes ADD COLUMN ${name} ${definition}`);
}
}
+12
View File
@@ -48,6 +48,18 @@ export const ISAC_DATA_TABLES = [
'gear_mod_quality_values',
'gear_mod_secondary_options',
'gear_mod_secondary_quality_values',
'specialization_progression_config',
'specialization_rank_cost_profiles',
'upgrade_resources',
'specializations',
'specialization_focuses',
'specialization_abilities',
'specialization_ability_effects',
'specialization_focus_abilities',
'specialization_tree_nodes',
'specialization_tree_node_ranks',
'specialization_tree_rank_effects',
'specialization_tree_rank_costs',
'game_data_icons',
'managed_images'
];