Files
shd.build/src/lib/server/shared-build-combat.ts
T
jb dd84dff055
Validate and deploy production / validate (push) Successful in 23s
Validate and deploy staging / validate (push) Successful in 50s
Validate and deploy production / deploy (push) Failing after 40s
Validate and deploy staging / deploy (push) Failing after 7s
feat: add community publishing and news syndication
2026-08-01 21:42:09 -04:00

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,
},
};
}