feat: add source-owned combat mechanic programs

This commit is contained in:
JB
2026-07-27 13:35:00 -04:00
parent 5a67b252aa
commit 4eb754e7c8
62 changed files with 14815 additions and 905 deletions
+39 -9
View File
@@ -34,13 +34,13 @@ export const load: PageServerLoad = async ({ cookies, url }) => {
const [
attributes, gear, gearBrands, gearTalents, gearAugments, gearAugmentCosts, brands, brandEffects,
weapons, weaponRanges, weaponTalentOptions, weaponBonuses,
talents, talentTiers, talentEffects, talentTemplates,
talents, talentTiers, talentEffects, talentTemplates, conditionalRules, conditionalPools, conditionalClauses, conditionalOutcomes,
chipSets, chipTwoPiece, chipPrimary, chipSecondary, chipTalents, chipTalentEffects,
protocols, protocolPrimary, protocolProfiles, protocolSecondary, protocolTalents, protocolTalentEffects, coreRanges,
icons, specializations, specializationFocuses, specializationAbilities, focusAbilities,
icons, specializations, specializationFocuses, specializationAbilities, specializationAbilityAttacks, focusAbilities,
specializationNodes, specializationRanks, specializationRankEffects, specializationRankCosts,
specializationCombatEffects, specializationRankCombatEffects, specializationCombatModifiers,
progression, rankProfiles, activeCalculationRevision, calculationStages, calculationProfiles
progression, rankProfiles, 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(),
@@ -58,6 +58,10 @@ export const load: PageServerLoad = async ({ cookies, url }) => {
(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_primary_options')).all(),
@@ -75,6 +79,7 @@ export const load: PageServerLoad = async ({ cookies, url }) => {
(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(),
@@ -90,6 +95,9 @@ export const load: PageServerLoad = async ({ cookies, url }) => {
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()
]);
@@ -97,6 +105,16 @@ export const load: PageServerLoad = async ({ cookies, url }) => {
const icon = icons.find((item) => item.entity_type === type && item.entity_id === id);
return icon ? gameDataIconUrl(type, String(id), icon.updated_at) : '';
};
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,
iconUrl: iconUrl('talent', talent.id),
@@ -104,7 +122,8 @@ export const load: PageServerLoad = async ({ cookies, url }) => {
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 {
viewer: viewer ? { id: viewer.id, displayName: viewer.displayName } : null,
@@ -141,7 +160,11 @@ export const load: PageServerLoad = async ({ cookies, url }) => {
twoPiece: chipTwoPiece.find((item) => item.skill_chip_set_id === set.id) ?? null,
primaryStatIds: chipPrimary.filter((item) => item.skill_chip_set_id === set.id).map((item) => String(item.stat_attribute_id)),
secondaryStatIds: chipSecondary.filter((item) => item.skill_chip_set_id === set.id).map((item) => String(item.stat_attribute_id)),
talent: talent ? { ...talent, effects: chipTalentEffects.filter((effect) => effect.talent_id === talent.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) => {
@@ -152,7 +175,11 @@ export const load: PageServerLoad = async ({ cookies, url }) => {
iconUrl: iconUrl('os_protocol', protocol.id),
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) } : null
talent: talent ? {
...talent,
effects: protocolTalentEffects.filter((effect) => effect.talent_id === talent.id),
rule: hydrateRule('os_protocol_talent', talent.id)
} : null
};
}),
specializations: specializations.map((specialization) => ({
@@ -166,7 +193,8 @@ export const load: PageServerLoad = async ({ cookies, url }) => {
return ability ? {
...ability,
slot: Number(link.ability_slot),
iconUrl: iconUrl('specialization_ability', ability.id)
iconUrl: iconUrl('specialization_ability', ability.id),
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) => ({
@@ -181,7 +209,8 @@ export const load: PageServerLoad = async ({ cookies, url }) => {
.map((effect) => ({
...effect,
definition: specializationCombatEffects.find((definition) => definition.id === effect.combat_effect_id),
modifiers: specializationCombatModifiers.filter((modifier) => modifier.rank_combat_effect_id === effect.id)
modifiers: specializationCombatModifiers.filter((modifier) => modifier.rank_combat_effect_id === effect.id),
rule: hydrateRule('specialization_tree_rank', effect.id)
}))
}))
}));
@@ -196,7 +225,8 @@ export const load: PageServerLoad = async ({ cookies, url }) => {
calculation: {
revision: activeCalculationRevision,
stages: calculationStages,
profiles: calculationProfiles
profiles: calculationProfiles,
skillProfiles: skillCalculationProfiles
}
};
};