feat: refine builder skill chip flows
This commit is contained in:
@@ -187,6 +187,14 @@ async function focusSnapshot(database: Database, id: string) {
|
||||
async function abilitySnapshot(database: Database, id: string) {
|
||||
return {
|
||||
record: await auditRecord('specialization_abilities', id),
|
||||
skillChipLinks: await (
|
||||
await database.prepare(
|
||||
`SELECT ability_id,compatible_ability_id
|
||||
FROM specialization_ability_skill_chip_links
|
||||
WHERE ability_id=? OR compatible_ability_id=?
|
||||
ORDER BY ability_id,compatible_ability_id`,
|
||||
)
|
||||
).all(id, id),
|
||||
attack:
|
||||
(await (
|
||||
await database.prepare(
|
||||
@@ -398,6 +406,7 @@ export const load: PageServerLoad = async ({ cookies, url }) => {
|
||||
'cooldownSeconds',
|
||||
'effectsJson',
|
||||
'attackJson',
|
||||
'skillChipCompatibleAbilityId',
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -470,6 +479,7 @@ export const load: PageServerLoad = async ({ cookies, url }) => {
|
||||
specializations,
|
||||
focuses,
|
||||
abilities,
|
||||
abilitySkillChipLinks,
|
||||
abilityAttacks,
|
||||
abilityEffects,
|
||||
focusAbilities,
|
||||
@@ -506,6 +516,11 @@ export const load: PageServerLoad = async ({ cookies, url }) => {
|
||||
'SELECT * FROM specialization_abilities ORDER BY specialization_id,active DESC,name COLLATE NOCASE',
|
||||
)
|
||||
).all(),
|
||||
(
|
||||
await database.prepare(
|
||||
'SELECT * FROM specialization_ability_skill_chip_links ORDER BY ability_id,compatible_ability_id',
|
||||
)
|
||||
).all(),
|
||||
(
|
||||
await database.prepare('SELECT * FROM specialization_ability_attacks')
|
||||
).all(),
|
||||
@@ -672,6 +687,17 @@ export const load: PageServerLoad = async ({ cookies, url }) => {
|
||||
})),
|
||||
...focuses,
|
||||
];
|
||||
const linkedAbilityById = new Map<string, string>();
|
||||
for (const link of abilitySkillChipLinks) {
|
||||
linkedAbilityById.set(
|
||||
String(link.ability_id),
|
||||
String(link.compatible_ability_id),
|
||||
);
|
||||
linkedAbilityById.set(
|
||||
String(link.compatible_ability_id),
|
||||
String(link.ability_id),
|
||||
);
|
||||
}
|
||||
const loadedAbilities = [
|
||||
...pendingAbilities.map((item) => ({
|
||||
...item,
|
||||
@@ -680,6 +706,7 @@ export const load: PageServerLoad = async ({ cookies, url }) => {
|
||||
cooldown_seconds: item.cooldownSeconds || null,
|
||||
attack: item.attackJson ? JSON.parse(String(item.attackJson)) : null,
|
||||
effects: parseJsonArray<AbilityEffectInput>(item.effectsJson),
|
||||
skillChipCompatibleAbilityId: item.skillChipCompatibleAbilityId || '',
|
||||
active: 1,
|
||||
pending: true,
|
||||
skillChipCount: 0,
|
||||
@@ -698,6 +725,8 @@ export const load: PageServerLoad = async ({ cookies, url }) => {
|
||||
chipCounts.find((item) => item.specialization_ability_id === ability.id)
|
||||
?.count ?? 0,
|
||||
),
|
||||
skillChipCompatibleAbilityId:
|
||||
linkedAbilityById.get(String(ability.id)) ?? '',
|
||||
iconUrl: iconUrl('specialization_ability', String(ability.id)),
|
||||
renderUrl: renderUrl('specialization_ability', String(ability.id)),
|
||||
})),
|
||||
@@ -1160,7 +1189,12 @@ export const actions: Actions = {
|
||||
);
|
||||
const form = await request.formData();
|
||||
const id = String(form.get('id') ?? '');
|
||||
const abilityId =
|
||||
id || String(form.get('_proposedEntityId') ?? '') || randomUUID();
|
||||
const specializationId = String(form.get('specializationId') ?? '');
|
||||
const skillChipCompatibleAbilityId = String(
|
||||
form.get('skillChipCompatibleAbilityId') ?? '',
|
||||
);
|
||||
const name = cleanName(form.get('name'));
|
||||
const description = cleanName(form.get('description'));
|
||||
const durationSeconds = optionalPositive(form.get('durationSeconds'));
|
||||
@@ -1312,6 +1346,46 @@ export const actions: Actions = {
|
||||
) {
|
||||
return fail(400, { abilityError: 'Choose an active specialization.' });
|
||||
}
|
||||
if (skillChipCompatibleAbilityId) {
|
||||
if (skillChipCompatibleAbilityId === abilityId) {
|
||||
return fail(400, {
|
||||
abilityError: 'An ability cannot share Skill Chips with itself.',
|
||||
});
|
||||
}
|
||||
const compatibleAbility = await (
|
||||
await database.prepare(
|
||||
'SELECT id,specialization_id FROM specialization_abilities WHERE id=? AND active=1',
|
||||
)
|
||||
).get(skillChipCompatibleAbilityId);
|
||||
if (
|
||||
!compatibleAbility ||
|
||||
String(compatibleAbility.specialization_id) !== specializationId
|
||||
) {
|
||||
return fail(400, {
|
||||
abilityError:
|
||||
'Choose an active Skill Chip-compatible ability from the same specialization.',
|
||||
});
|
||||
}
|
||||
const conflictingLink = await (
|
||||
await database.prepare(
|
||||
`SELECT ability_id,compatible_ability_id
|
||||
FROM specialization_ability_skill_chip_links
|
||||
WHERE (ability_id=? OR compatible_ability_id=?)
|
||||
AND ability_id<>? AND compatible_ability_id<>?`,
|
||||
)
|
||||
).get(
|
||||
skillChipCompatibleAbilityId,
|
||||
skillChipCompatibleAbilityId,
|
||||
abilityId,
|
||||
abilityId,
|
||||
);
|
||||
if (conflictingLink) {
|
||||
return fail(409, {
|
||||
abilityError:
|
||||
'Each ability can share one Skill Chip pool. Clear the existing link first.',
|
||||
});
|
||||
}
|
||||
}
|
||||
if (
|
||||
!(await validateAttributeIds(
|
||||
database,
|
||||
@@ -1387,8 +1461,6 @@ export const actions: Actions = {
|
||||
: 'The ability icon could not be processed.',
|
||||
});
|
||||
}
|
||||
const abilityId =
|
||||
id || String(form.get('_proposedEntityId') ?? '') || randomUUID();
|
||||
const before = id ? await abilitySnapshot(database, id) : null;
|
||||
const iconChanged = Boolean(iconMutation.output || iconMutation.remove);
|
||||
const previousIcon = iconChanged
|
||||
@@ -1426,6 +1498,24 @@ export const actions: Actions = {
|
||||
now,
|
||||
);
|
||||
}
|
||||
await (
|
||||
await database.prepare(
|
||||
`DELETE FROM specialization_ability_skill_chip_links
|
||||
WHERE ability_id=? OR compatible_ability_id=?`,
|
||||
)
|
||||
).run(abilityId, abilityId);
|
||||
if (skillChipCompatibleAbilityId) {
|
||||
const [leftAbilityId, rightAbilityId] = [
|
||||
abilityId,
|
||||
skillChipCompatibleAbilityId,
|
||||
].sort();
|
||||
await (
|
||||
await database.prepare(
|
||||
`INSERT INTO specialization_ability_skill_chip_links
|
||||
(ability_id,compatible_ability_id) VALUES(?,?)`,
|
||||
)
|
||||
).run(leftAbilityId, rightAbilityId);
|
||||
}
|
||||
await (
|
||||
await database.prepare(
|
||||
'DELETE FROM specialization_ability_effects WHERE ability_id=?',
|
||||
@@ -2326,6 +2416,12 @@ export const actions: Actions = {
|
||||
'UPDATE specialization_abilities SET active=0,updated_at=? WHERE id=?',
|
||||
)
|
||||
).run(new Date().toISOString(), id);
|
||||
await (
|
||||
await database.prepare(
|
||||
`DELETE FROM specialization_ability_skill_chip_links
|
||||
WHERE ability_id=? OR compatible_ability_id=?`,
|
||||
)
|
||||
).run(id, id);
|
||||
if (ability)
|
||||
await refreshSpecializationFocuses(
|
||||
database,
|
||||
|
||||
Reference in New Issue
Block a user