389 lines
12 KiB
TypeScript
389 lines
12 KiB
TypeScript
import type { Database } from '@tursodatabase/database';
|
|
import { attachWeaponCategoryBonuses } from '$lib/weapons/category-bonuses';
|
|
|
|
export async function loadSharedBuildCombatCatalog(database: Database) {
|
|
const [
|
|
attributes,
|
|
gear,
|
|
gearAugments,
|
|
brands,
|
|
brandEffects,
|
|
weapons,
|
|
weaponBonuses,
|
|
talents,
|
|
talentTiers,
|
|
talentEffects,
|
|
talentTemplates,
|
|
conditionalRules,
|
|
conditionalPools,
|
|
conditionalClauses,
|
|
conditionalOutcomes,
|
|
chipSets,
|
|
chipTwoPiece,
|
|
chipTalents,
|
|
chipTalentEffects,
|
|
protocols,
|
|
protocolPrimary,
|
|
protocolProfiles,
|
|
protocolSecondary,
|
|
protocolTalents,
|
|
protocolTalentEffects,
|
|
specializations,
|
|
specializationFocuses,
|
|
specializationAbilities,
|
|
specializationAbilityAttacks,
|
|
focusAbilities,
|
|
specializationNodes,
|
|
specializationRanks,
|
|
specializationRankEffects,
|
|
specializationCombatEffects,
|
|
specializationRankCombatEffects,
|
|
specializationCombatModifiers,
|
|
activeCalculationRevision,
|
|
calculationStages,
|
|
calculationProfiles,
|
|
skillCalculationProfiles,
|
|
] = await Promise.all([
|
|
(
|
|
await database.prepare(
|
|
'SELECT * FROM stat_attributes WHERE active=1 ORDER BY name COLLATE NOCASE',
|
|
)
|
|
).all(),
|
|
(
|
|
await database.prepare(
|
|
'SELECT * FROM gear WHERE active=1 ORDER BY slot,name COLLATE NOCASE',
|
|
)
|
|
).all(),
|
|
(
|
|
await database.prepare(
|
|
'SELECT * FROM gear_tactical_augments ORDER BY gear_slot,augment_slot',
|
|
)
|
|
).all(),
|
|
(
|
|
await database.prepare(
|
|
'SELECT * FROM brand_sets WHERE active=1 ORDER BY name COLLATE NOCASE',
|
|
)
|
|
).all(),
|
|
(
|
|
await database.prepare(
|
|
'SELECT * FROM brand_set_effects ORDER BY pieces_required',
|
|
)
|
|
).all(),
|
|
(
|
|
await database.prepare(
|
|
'SELECT * FROM weapons WHERE active=1 ORDER BY category,name COLLATE NOCASE',
|
|
)
|
|
).all(),
|
|
(
|
|
await database.prepare(
|
|
'SELECT * FROM weapon_category_attribute_bonuses ORDER BY weapon_category,sort_order',
|
|
)
|
|
).all(),
|
|
(
|
|
await database.prepare(
|
|
'SELECT * FROM talents WHERE active=1 ORDER BY name COLLATE NOCASE',
|
|
)
|
|
).all(),
|
|
(await database.prepare('SELECT * FROM talent_tiers ORDER BY tier')).all(),
|
|
(await database.prepare('SELECT * FROM talent_tier_effects')).all(),
|
|
(
|
|
await database.prepare('SELECT * FROM talent_description_templates')
|
|
).all(),
|
|
(
|
|
await database.prepare(
|
|
'SELECT * FROM conditional_effect_rules WHERE configured=1',
|
|
)
|
|
).all(),
|
|
(
|
|
await database.prepare(
|
|
'SELECT * FROM conditional_effect_state_pools ORDER BY rule_id,sort_order',
|
|
)
|
|
).all(),
|
|
(
|
|
await database.prepare(
|
|
'SELECT * FROM conditional_effect_clauses WHERE active=1 ORDER BY rule_id,sort_order',
|
|
)
|
|
).all(),
|
|
(
|
|
await database.prepare(
|
|
'SELECT * FROM conditional_effect_outcomes ORDER BY rule_id,variant_key,outcome_key,sort_order',
|
|
)
|
|
).all(),
|
|
(
|
|
await database.prepare(
|
|
'SELECT * FROM skill_chip_sets WHERE active=1 ORDER BY name COLLATE NOCASE',
|
|
)
|
|
).all(),
|
|
(
|
|
await database.prepare('SELECT * FROM skill_chip_two_piece_bonuses')
|
|
).all(),
|
|
(await database.prepare('SELECT * FROM skill_chip_talents')).all(),
|
|
(
|
|
await database.prepare(
|
|
'SELECT * FROM skill_chip_talent_effects ORDER BY sort_order',
|
|
)
|
|
).all(),
|
|
(
|
|
await database.prepare(
|
|
'SELECT * FROM os_protocols WHERE active=1 ORDER BY name COLLATE NOCASE',
|
|
)
|
|
).all(),
|
|
(await database.prepare('SELECT * FROM os_protocol_primary_effects')).all(),
|
|
(
|
|
await database.prepare('SELECT * FROM os_protocol_secondary_profiles')
|
|
).all(),
|
|
(
|
|
await database.prepare(
|
|
'SELECT * FROM os_protocol_secondary_effects ORDER BY effect_role',
|
|
)
|
|
).all(),
|
|
(await database.prepare('SELECT * FROM os_protocol_talents')).all(),
|
|
(
|
|
await database.prepare(
|
|
'SELECT * FROM os_protocol_talent_effects ORDER BY sort_order',
|
|
)
|
|
).all(),
|
|
(
|
|
await database.prepare(
|
|
'SELECT * FROM specializations WHERE active=1 ORDER BY name COLLATE NOCASE',
|
|
)
|
|
).all(),
|
|
(
|
|
await database.prepare(
|
|
'SELECT * FROM specialization_focuses WHERE active=1 ORDER BY specialization_id,display_order,name COLLATE NOCASE',
|
|
)
|
|
).all(),
|
|
(
|
|
await database.prepare(
|
|
'SELECT * FROM specialization_abilities WHERE active=1 ORDER BY specialization_id,name COLLATE NOCASE',
|
|
)
|
|
).all(),
|
|
(
|
|
await database.prepare('SELECT * FROM specialization_ability_attacks')
|
|
).all(),
|
|
(
|
|
await database.prepare(
|
|
'SELECT * FROM specialization_focus_abilities ORDER BY focus_id,ability_slot',
|
|
)
|
|
).all(),
|
|
(
|
|
await database.prepare(
|
|
'SELECT * FROM specialization_tree_nodes WHERE active=1 ORDER BY focus_id,row_number,column_number',
|
|
)
|
|
).all(),
|
|
(
|
|
await database.prepare(
|
|
'SELECT * FROM specialization_tree_node_ranks ORDER BY node_id,rank',
|
|
)
|
|
).all(),
|
|
(
|
|
await database.prepare(
|
|
'SELECT * FROM specialization_tree_rank_effects ORDER BY node_id,rank,effect_order',
|
|
)
|
|
).all(),
|
|
(
|
|
await database.prepare(
|
|
'SELECT * FROM specialization_combat_effects WHERE active=1 ORDER BY specialization_id,name COLLATE NOCASE',
|
|
)
|
|
).all(),
|
|
(
|
|
await database.prepare(
|
|
'SELECT * FROM specialization_tree_rank_combat_effects ORDER BY node_id,rank,effect_order',
|
|
)
|
|
).all(),
|
|
(
|
|
await database.prepare(
|
|
'SELECT * FROM specialization_tree_rank_combat_modifiers ORDER BY rank_combat_effect_id,modifier_order',
|
|
)
|
|
).all(),
|
|
(
|
|
await database.prepare(
|
|
"SELECT * FROM calculation_revisions WHERE status='active' ORDER BY version DESC LIMIT 1",
|
|
)
|
|
).get(),
|
|
(
|
|
await database.prepare(`SELECT stage.* FROM calculation_stages stage
|
|
JOIN calculation_revisions revision ON revision.id=stage.revision_id
|
|
WHERE revision.status='active' ORDER BY stage.display_order`)
|
|
).all(),
|
|
(
|
|
await database.prepare(`SELECT profile.* FROM attribute_calculation_profiles profile
|
|
JOIN calculation_revisions revision ON revision.id=profile.revision_id
|
|
WHERE revision.status='active' ORDER BY profile.stage_key,profile.stat_attribute_id`)
|
|
).all(),
|
|
(
|
|
await database.prepare(`SELECT profile.* FROM skill_attribute_calculation_profiles profile
|
|
JOIN calculation_revisions revision ON revision.id=profile.revision_id
|
|
WHERE revision.status='active' ORDER BY profile.stage_key,profile.stat_attribute_id`)
|
|
).all(),
|
|
]);
|
|
|
|
const hydrateRule = (sourceType: string, sourceId: unknown) => {
|
|
const rule = conditionalRules.find(
|
|
(item) => item.source_type === sourceType && item.source_id === sourceId,
|
|
);
|
|
return rule
|
|
? {
|
|
rule,
|
|
statePools: conditionalPools.filter(
|
|
(pool) => pool.rule_id === rule.id,
|
|
),
|
|
clauses: conditionalClauses.filter(
|
|
(clause) => clause.rule_id === rule.id,
|
|
),
|
|
outcomes: conditionalOutcomes.filter(
|
|
(outcome) => outcome.rule_id === rule.id,
|
|
),
|
|
triggers: [],
|
|
}
|
|
: null;
|
|
};
|
|
const configuredTalents = talents.map((talent) => ({
|
|
...talent,
|
|
template:
|
|
talentTemplates.find((item) => item.talent_id === talent.id)?.template ??
|
|
'',
|
|
tiers: talentTiers
|
|
.filter((tier) => tier.talent_id === talent.id)
|
|
.map((tier) => ({
|
|
...tier,
|
|
effects: talentEffects.filter(
|
|
(effect) => effect.talent_tier_id === tier.id,
|
|
),
|
|
})),
|
|
rule: hydrateRule('talent', talent.id),
|
|
}));
|
|
|
|
return {
|
|
attributes,
|
|
gear,
|
|
gearAugments,
|
|
brands: brands.map((brand) => ({
|
|
...brand,
|
|
effects: brandEffects.filter(
|
|
(effect) => effect.brand_set_id === brand.id,
|
|
),
|
|
})),
|
|
weapons: attachWeaponCategoryBonuses(weapons, weaponBonuses),
|
|
talents: configuredTalents,
|
|
chipSets: chipSets.map((set) => {
|
|
const talent = chipTalents.find(
|
|
(item) => item.skill_chip_set_id === set.id,
|
|
);
|
|
return {
|
|
...set,
|
|
twoPiece:
|
|
chipTwoPiece.find((item) => item.skill_chip_set_id === set.id) ??
|
|
null,
|
|
talent: talent
|
|
? {
|
|
...talent,
|
|
effects: chipTalentEffects.filter(
|
|
(effect) => effect.talent_id === talent.id,
|
|
),
|
|
rule: hydrateRule('skill_chip_talent', talent.id),
|
|
}
|
|
: null,
|
|
};
|
|
}),
|
|
protocols: protocols.map((protocol) => {
|
|
const profile = protocolProfiles.find(
|
|
(item) =>
|
|
item.quality === protocol.quality &&
|
|
item.primary_attribute_type === protocol.primary_attribute_type,
|
|
);
|
|
const talent = protocolTalents.find(
|
|
(item) => item.protocol_id === protocol.id,
|
|
);
|
|
return {
|
|
...protocol,
|
|
primaryEffect:
|
|
protocolPrimary.find((item) => item.protocol_id === protocol.id) ??
|
|
null,
|
|
secondaryEffects: profile
|
|
? protocolSecondary.filter(
|
|
(effect) => effect.profile_id === profile.id,
|
|
)
|
|
: [],
|
|
talent: talent
|
|
? {
|
|
...talent,
|
|
effects: protocolTalentEffects.filter(
|
|
(effect) => effect.talent_id === talent.id,
|
|
),
|
|
rule: hydrateRule('os_protocol_talent', talent.id),
|
|
}
|
|
: null,
|
|
};
|
|
}),
|
|
specializations: specializations.map((specialization) => ({
|
|
...specialization,
|
|
focuses: specializationFocuses
|
|
.filter((focus) => focus.specialization_id === specialization.id)
|
|
.map((focus) => {
|
|
const abilities = focusAbilities
|
|
.filter((link) => link.focus_id === focus.id)
|
|
.sort(
|
|
(left, right) =>
|
|
Number(left.ability_slot) - Number(right.ability_slot),
|
|
)
|
|
.map((link) => {
|
|
const ability = specializationAbilities.find(
|
|
(entry) => entry.id === link.ability_id,
|
|
);
|
|
return ability
|
|
? {
|
|
...ability,
|
|
attack:
|
|
specializationAbilityAttacks.find(
|
|
(attack) => attack.ability_id === ability.id,
|
|
) ?? null,
|
|
}
|
|
: null;
|
|
})
|
|
.filter(Boolean);
|
|
const nodes = specializationNodes
|
|
.filter((node) => node.focus_id === focus.id)
|
|
.map((node) => ({
|
|
...node,
|
|
ranks: specializationRanks
|
|
.filter((rank) => rank.node_id === node.id)
|
|
.map((rank) => ({
|
|
...rank,
|
|
effects: specializationRankEffects.filter(
|
|
(effect) =>
|
|
effect.node_id === node.id &&
|
|
Number(effect.rank) === Number(rank.rank),
|
|
),
|
|
combatEffects: specializationRankCombatEffects
|
|
.filter(
|
|
(effect) =>
|
|
effect.node_id === node.id &&
|
|
Number(effect.rank) === Number(rank.rank),
|
|
)
|
|
.map((effect) => ({
|
|
...effect,
|
|
definition: specializationCombatEffects.find(
|
|
(definition) =>
|
|
definition.id === effect.combat_effect_id,
|
|
),
|
|
modifiers: specializationCombatModifiers.filter(
|
|
(modifier) =>
|
|
modifier.rank_combat_effect_id === effect.id,
|
|
),
|
|
rule: hydrateRule('specialization_tree_rank', effect.id),
|
|
})),
|
|
})),
|
|
}));
|
|
return { ...focus, abilities, nodes };
|
|
}),
|
|
})),
|
|
calculation: {
|
|
revision: activeCalculationRevision ?? null,
|
|
stages: calculationStages,
|
|
profiles: calculationProfiles,
|
|
skillProfiles: skillCalculationProfiles,
|
|
},
|
|
};
|
|
}
|