feat: refine builder skill chip flows
This commit is contained in:
@@ -769,6 +769,44 @@ for (const statement of statements) {
|
||||
await database.exec(statement);
|
||||
}
|
||||
|
||||
await database.exec(`CREATE TRIGGER IF NOT EXISTS specialization_ability_skill_chip_links_validate_insert
|
||||
BEFORE INSERT ON specialization_ability_skill_chip_links
|
||||
BEGIN
|
||||
SELECT CASE WHEN EXISTS (
|
||||
SELECT 1 FROM specialization_ability_skill_chip_links link
|
||||
WHERE link.ability_id IN (NEW.ability_id,NEW.compatible_ability_id)
|
||||
OR link.compatible_ability_id IN (NEW.ability_id,NEW.compatible_ability_id)
|
||||
) THEN RAISE(ABORT,'Each ability can share one Skill Chip pool') END;
|
||||
SELECT CASE WHEN NOT EXISTS (
|
||||
SELECT 1 FROM specialization_abilities ability
|
||||
JOIN specialization_abilities compatible
|
||||
ON compatible.id=NEW.compatible_ability_id
|
||||
WHERE ability.id=NEW.ability_id
|
||||
AND ability.active=1
|
||||
AND compatible.active=1
|
||||
AND ability.specialization_id=compatible.specialization_id
|
||||
) THEN RAISE(ABORT,'Skill Chip sharing requires active abilities from one specialization') END;
|
||||
END`);
|
||||
await database.exec(`CREATE TRIGGER IF NOT EXISTS specialization_ability_skill_chip_links_validate_update
|
||||
BEFORE UPDATE ON specialization_ability_skill_chip_links
|
||||
BEGIN
|
||||
SELECT CASE WHEN EXISTS (
|
||||
SELECT 1 FROM specialization_ability_skill_chip_links link
|
||||
WHERE link.rowid<>OLD.rowid
|
||||
AND (link.ability_id IN (NEW.ability_id,NEW.compatible_ability_id)
|
||||
OR link.compatible_ability_id IN (NEW.ability_id,NEW.compatible_ability_id))
|
||||
) THEN RAISE(ABORT,'Each ability can share one Skill Chip pool') END;
|
||||
SELECT CASE WHEN NOT EXISTS (
|
||||
SELECT 1 FROM specialization_abilities ability
|
||||
JOIN specialization_abilities compatible
|
||||
ON compatible.id=NEW.compatible_ability_id
|
||||
WHERE ability.id=NEW.ability_id
|
||||
AND ability.active=1
|
||||
AND compatible.active=1
|
||||
AND ability.specialization_id=compatible.specialization_id
|
||||
) THEN RAISE(ABORT,'Skill Chip sharing requires active abilities from one specialization') END;
|
||||
END`);
|
||||
|
||||
const articleRevisionColumns = await (
|
||||
await database.prepare("PRAGMA table_info('article_revisions')")
|
||||
).all();
|
||||
|
||||
@@ -2,7 +2,11 @@ import { connect } from '@tursodatabase/database';
|
||||
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
||||
import { basename, dirname, join, resolve } from 'node:path';
|
||||
import process from 'node:process';
|
||||
import { ISAC_DATA_TABLES, quoteIdentifier } from './isac-data-tables.mjs';
|
||||
import {
|
||||
ISAC_DATA_FORMAT_VERSION,
|
||||
ISAC_DATA_TABLES,
|
||||
quoteIdentifier,
|
||||
} from './isac-data-tables.mjs';
|
||||
|
||||
const databasePath = resolve(
|
||||
process.env.TDR_DATABASE_PATH || './data/shd-build-development.db',
|
||||
@@ -67,7 +71,7 @@ for (const row of [
|
||||
|
||||
const snapshot = {
|
||||
format: 'shd-build-isac-data',
|
||||
version: 4,
|
||||
version: ISAC_DATA_FORMAT_VERSION,
|
||||
createdAt: new Date().toISOString(),
|
||||
sourceDatabase: basename(databasePath),
|
||||
totalRows,
|
||||
|
||||
@@ -3,7 +3,11 @@ import { spawnSync } from 'node:child_process';
|
||||
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
||||
import { basename, join, resolve } from 'node:path';
|
||||
import process from 'node:process';
|
||||
import { ISAC_DATA_TABLES, quoteIdentifier } from './isac-data-tables.mjs';
|
||||
import {
|
||||
ISAC_DATA_FORMAT_VERSION,
|
||||
ISAC_DATA_TABLES,
|
||||
quoteIdentifier,
|
||||
} from './isac-data-tables.mjs';
|
||||
|
||||
const snapshotArgument = process.argv
|
||||
.slice(2)
|
||||
@@ -22,7 +26,7 @@ const snapshotPath = resolve(snapshotArgument);
|
||||
const snapshot = JSON.parse(await readFile(snapshotPath, 'utf8'));
|
||||
if (
|
||||
snapshot?.format !== 'shd-build-isac-data' ||
|
||||
snapshot?.version !== 4 ||
|
||||
snapshot?.version !== ISAC_DATA_FORMAT_VERSION ||
|
||||
!Array.isArray(snapshot.tables) ||
|
||||
!Array.isArray(snapshot.icons)
|
||||
) {
|
||||
@@ -39,6 +43,87 @@ if (
|
||||
'The ISAC snapshot table manifest is incomplete or out of order.',
|
||||
);
|
||||
}
|
||||
const abilityRows = snapshot.tables.find(
|
||||
(table) => table.name === 'specialization_abilities',
|
||||
).rows;
|
||||
const abilitiesById = new Map(
|
||||
abilityRows.map((row) => [String(row.id ?? ''), row]),
|
||||
);
|
||||
const skillChipRows = snapshot.tables.find(
|
||||
(table) => table.name === 'skill_chip_sets',
|
||||
).rows;
|
||||
const activeSkillChipCounts = new Map();
|
||||
for (const row of skillChipRows) {
|
||||
const abilityId = String(row.specialization_ability_id ?? '');
|
||||
if (!abilityId || !abilitiesById.has(abilityId)) {
|
||||
throw new Error(
|
||||
`The ISAC snapshot contains a Skill Chip definition with a missing ability reference "${abilityId}".`,
|
||||
);
|
||||
}
|
||||
if (Number(row.active) !== 1) continue;
|
||||
const count = (activeSkillChipCounts.get(abilityId) ?? 0) + 1;
|
||||
if (count > 3) {
|
||||
throw new Error(
|
||||
`The ISAC snapshot assigns more than three active Skill Chip definitions to ability "${abilityId}".`,
|
||||
);
|
||||
}
|
||||
activeSkillChipCounts.set(abilityId, count);
|
||||
}
|
||||
const skillChipLinks = snapshot.tables.find(
|
||||
(table) => table.name === 'specialization_ability_skill_chip_links',
|
||||
).rows;
|
||||
const linkedAbilityIds = new Set();
|
||||
for (const row of skillChipLinks) {
|
||||
const abilityId = row.ability_id;
|
||||
const compatibleAbilityId = row.compatible_ability_id;
|
||||
if (
|
||||
typeof abilityId !== 'string' ||
|
||||
!abilityId ||
|
||||
typeof compatibleAbilityId !== 'string' ||
|
||||
!compatibleAbilityId
|
||||
) {
|
||||
throw new Error(
|
||||
'The ISAC snapshot contains a Skill Chip sharing link with malformed ability references.',
|
||||
);
|
||||
}
|
||||
if (abilityId === compatibleAbilityId) {
|
||||
throw new Error(
|
||||
`The ISAC snapshot contains a self-referencing Skill Chip sharing link for ability "${abilityId}".`,
|
||||
);
|
||||
}
|
||||
if (abilityId >= compatibleAbilityId) {
|
||||
throw new Error(
|
||||
`The ISAC snapshot contains a Skill Chip sharing link that is not in canonical ability order: "${abilityId}", "${compatibleAbilityId}".`,
|
||||
);
|
||||
}
|
||||
const ability = abilitiesById.get(abilityId);
|
||||
const compatibleAbility = abilitiesById.get(compatibleAbilityId);
|
||||
if (!ability || !compatibleAbility) {
|
||||
throw new Error(
|
||||
`The ISAC snapshot contains a Skill Chip sharing link with a missing ability reference: "${abilityId}", "${compatibleAbilityId}".`,
|
||||
);
|
||||
}
|
||||
if (Number(ability.active) !== 1 || Number(compatibleAbility.active) !== 1) {
|
||||
throw new Error(
|
||||
`The ISAC snapshot contains a Skill Chip sharing link with an inactive ability: "${abilityId}", "${compatibleAbilityId}".`,
|
||||
);
|
||||
}
|
||||
if (ability.specialization_id !== compatibleAbility.specialization_id) {
|
||||
throw new Error(
|
||||
`The ISAC snapshot contains a cross-specialization Skill Chip sharing link: "${abilityId}", "${compatibleAbilityId}".`,
|
||||
);
|
||||
}
|
||||
if (
|
||||
linkedAbilityIds.has(abilityId) ||
|
||||
linkedAbilityIds.has(compatibleAbilityId)
|
||||
) {
|
||||
throw new Error(
|
||||
'The ISAC snapshot contains more than one Skill Chip sharing link touching an ability.',
|
||||
);
|
||||
}
|
||||
linkedAbilityIds.add(abilityId);
|
||||
linkedAbilityIds.add(compatibleAbilityId);
|
||||
}
|
||||
for (const kind of ['weapon', 'gear']) {
|
||||
const options = snapshot.tables.find(
|
||||
(table) => table.name === `${kind}_mod_secondary_options`,
|
||||
|
||||
@@ -57,6 +57,7 @@ export const ISAC_DATA_TABLES = [
|
||||
'specialization_combat_effects',
|
||||
'specialization_focuses',
|
||||
'specialization_abilities',
|
||||
'specialization_ability_skill_chip_links',
|
||||
'specialization_ability_attacks',
|
||||
'specialization_ability_effects',
|
||||
'specialization_focus_abilities',
|
||||
@@ -75,5 +76,7 @@ export const ISAC_DATA_TABLES = [
|
||||
'managed_images',
|
||||
];
|
||||
|
||||
export const ISAC_DATA_FORMAT_VERSION = 5;
|
||||
|
||||
export const quoteIdentifier = (identifier) =>
|
||||
`"${String(identifier).replaceAll('"', '""')}"`;
|
||||
|
||||
Reference in New Issue
Block a user