feat: expand ISAC content modeling
This commit is contained in:
@@ -40,6 +40,10 @@ import { recordIsacAudit } from '$lib/server/audit';
|
||||
import { removePublicComment } from '$lib/server/comment-removal';
|
||||
import { createNotification } from '$lib/server/notifications';
|
||||
import type { Actions } from './$types';
|
||||
import {
|
||||
publicWeaponModSlots,
|
||||
weaponModSlotDefinitionByName,
|
||||
} from '$lib/weapons/mod-slots';
|
||||
|
||||
const number = (value: unknown) => {
|
||||
const numeric = Number(value);
|
||||
@@ -336,8 +340,7 @@ export const load: PageServerLoad = async ({ params, url, cookies }) => {
|
||||
rowsSql = `SELECT item.*,render.id AS render_id,render.updated_at AS render_updated_at,
|
||||
native.damage_min,native.damage_max,native.reference_level,
|
||||
(SELECT COUNT(*) FROM weapon_talent_options options WHERE options.weapon_id=item.id AND options.talent_slot=1) AS talent_1_count,
|
||||
(SELECT COUNT(*) FROM weapon_talent_options options WHERE options.weapon_id=item.id AND options.talent_slot=2) AS talent_2_count,
|
||||
(SELECT COUNT(*) FROM weapon_category_attribute_bonuses bonuses WHERE bonuses.weapon_category=item.category) AS bonus_count
|
||||
(SELECT COUNT(*) FROM weapon_talent_options options WHERE options.weapon_id=item.id AND options.talent_slot=2) AS talent_2_count
|
||||
FROM weapons item
|
||||
LEFT JOIN weapon_tier_ranges native ON native.weapon_id=item.id AND native.tier=item.native_tier
|
||||
LEFT JOIN managed_images render ON render.source_entity_type='weapon_render' AND render.source_entity_id=item.id
|
||||
@@ -386,7 +389,7 @@ export const load: PageServerLoad = async ({ params, url, cookies }) => {
|
||||
rowsSql = `SELECT item.*,weapons.name AS exotic_weapon,
|
||||
render.id AS render_id,render.updated_at AS render_updated_at,
|
||||
GROUP_CONCAT(DISTINCT categories.weapon_category) AS categories,
|
||||
(SELECT COUNT(*) FROM weapon_mod_secondary_options secondary WHERE secondary.weapon_mod_id=item.id) AS secondary_count,
|
||||
(SELECT COUNT(*) FROM weapon_mod_secondary_options) AS secondary_count,
|
||||
(SELECT COUNT(*) FROM weapon_mod_effects effects WHERE effects.weapon_mod_id=item.id) AS effect_count
|
||||
FROM weapon_mods item
|
||||
LEFT JOIN weapons ON weapons.id=item.exotic_weapon_id
|
||||
@@ -398,7 +401,7 @@ export const load: PageServerLoad = async ({ params, url, cookies }) => {
|
||||
case 'gear-mods':
|
||||
countSql = `SELECT COUNT(*) AS total FROM gear_mods item WHERE item.active=1${searchClause}`;
|
||||
rowsSql = `SELECT item.*,render.id AS render_id,render.updated_at AS render_updated_at,
|
||||
(SELECT COUNT(*) FROM gear_mod_secondary_options secondary WHERE secondary.gear_mod_id=item.id) AS secondary_count,
|
||||
(SELECT COUNT(*) FROM gear_mod_secondary_options) AS secondary_count,
|
||||
(SELECT MIN(primary_level_1_min) FROM gear_mod_quality_values quality WHERE quality.gear_mod_id=item.id) AS level_1_floor,
|
||||
(SELECT MAX(primary_level_20_max) FROM gear_mod_quality_values quality WHERE quality.gear_mod_id=item.id) AS level_20_ceiling
|
||||
FROM gear_mods item
|
||||
@@ -409,7 +412,7 @@ export const load: PageServerLoad = async ({ params, url, cookies }) => {
|
||||
case 'os-protocols':
|
||||
countSql = `SELECT COUNT(*) AS total FROM os_protocols item WHERE item.active=1${searchClause}`;
|
||||
rowsSql = `SELECT item.*,attributes.name AS primary_stat,attributes.unit AS primary_unit,
|
||||
primary_effect.base_value,talents.name AS talent_name,icons.updated_at AS icon_updated_at,
|
||||
primary_effect.base_value,talents.name AS talent_name,talents.description_template AS talent_description,icons.updated_at AS icon_updated_at,
|
||||
render.id AS render_id,render.updated_at AS render_updated_at
|
||||
FROM os_protocols item
|
||||
LEFT JOIN os_protocol_primary_effects primary_effect ON primary_effect.protocol_id=item.id
|
||||
@@ -500,8 +503,8 @@ export const load: PageServerLoad = async ({ params, url, cookies }) => {
|
||||
countSql = `SELECT COUNT(*) AS total FROM skill_chip_sets item WHERE item.active=1${searchClause}`;
|
||||
rowsSql = `SELECT item.*,bonus.min_value,bonus.max_value,attributes.name AS bonus_stat,attributes.unit AS bonus_unit,
|
||||
talents.name AS talent_name,abilities.name AS ability_name,specializations.name AS specialization_name,
|
||||
(SELECT COUNT(*) FROM skill_chip_primary_options options WHERE options.skill_chip_set_id=item.id) AS primary_count,
|
||||
(SELECT COUNT(*) FROM skill_chip_secondary_options options WHERE options.skill_chip_set_id=item.id) AS secondary_count,
|
||||
(SELECT COUNT(*) FROM skill_chip_primary_options) AS primary_count,
|
||||
(SELECT COUNT(*) FROM skill_chip_secondary_options) AS secondary_count,
|
||||
icons.updated_at AS icon_updated_at,render.id AS render_id,render.updated_at AS render_updated_at
|
||||
FROM skill_chip_sets item
|
||||
LEFT JOIN skill_chip_two_piece_bonuses bonus ON bonus.skill_chip_set_id=item.id
|
||||
@@ -549,6 +552,17 @@ export const load: PageServerLoad = async ({ params, url, cookies }) => {
|
||||
const rows = await (
|
||||
await database.prepare(rowsSql)
|
||||
).all(...queryArgs, pagination.perPage, pagination.offset);
|
||||
const protocolTalentEffects =
|
||||
category.slug === 'os-protocols' && rows.length
|
||||
? await (
|
||||
await database.prepare(`SELECT talents.protocol_id,effects.base_value,attributes.unit
|
||||
FROM os_protocol_talents talents
|
||||
JOIN os_protocol_talent_effects effects ON effects.talent_id=talents.id
|
||||
JOIN stat_attributes attributes ON attributes.id=effects.stat_attribute_id
|
||||
WHERE talents.protocol_id IN (${rows.map(() => '?').join(',')})
|
||||
ORDER BY talents.protocol_id,effects.sort_order`)
|
||||
).all(...rows.map((row) => row.id))
|
||||
: [];
|
||||
const weaponRelations =
|
||||
category.slug === 'weapons' && rows.length
|
||||
? await (
|
||||
@@ -563,6 +577,22 @@ export const load: PageServerLoad = async ({ params, url, cookies }) => {
|
||||
ORDER BY options.talent_slot,talents.name COLLATE NOCASE`)
|
||||
).all(...rows.map((row) => row.id))
|
||||
: [];
|
||||
const weaponModSlotRows =
|
||||
category.slug === 'weapons' && rows.length
|
||||
? await (
|
||||
await database.prepare(`SELECT weapon_id,tier,slot_type FROM weapon_mod_slots
|
||||
WHERE weapon_id IN (${rows.map(() => '?').join(',')})
|
||||
ORDER BY weapon_id,tier,slot_type`)
|
||||
).all(...rows.map((row) => row.id))
|
||||
: [];
|
||||
const weaponModSlotIconRows =
|
||||
category.slug === 'weapons'
|
||||
? await (
|
||||
await database.prepare(
|
||||
"SELECT entity_id,updated_at FROM game_data_icons WHERE entity_type='weapon_mod_slot'",
|
||||
)
|
||||
).all()
|
||||
: [];
|
||||
const gearRelations =
|
||||
category.slug === 'gear' && rows.length
|
||||
? await (
|
||||
@@ -769,6 +799,36 @@ export const load: PageServerLoad = async ({ params, url, cookies }) => {
|
||||
const relatedTalents = weaponRelations.filter(
|
||||
(relation) => relation.weapon_id === row.id,
|
||||
);
|
||||
const modSlots = publicWeaponModSlots(
|
||||
Number(row.native_tier),
|
||||
[0, 1, 2].map((tier) => ({
|
||||
tier,
|
||||
slots: weaponModSlotRows
|
||||
.filter(
|
||||
(slot) =>
|
||||
slot.weapon_id === row.id && Number(slot.tier) === tier,
|
||||
)
|
||||
.map((slot) => String(slot.slot_type)),
|
||||
})),
|
||||
).map((slot) => {
|
||||
const definition = weaponModSlotDefinitionByName(slot.slot);
|
||||
const icon = definition
|
||||
? weaponModSlotIconRows.find(
|
||||
(row) => row.entity_id === definition.id,
|
||||
)
|
||||
: null;
|
||||
return {
|
||||
...slot,
|
||||
iconUrl:
|
||||
definition && icon
|
||||
? gameDataIconUrl(
|
||||
'weapon_mod_slot',
|
||||
definition.id,
|
||||
icon.updated_at,
|
||||
)
|
||||
: '',
|
||||
};
|
||||
});
|
||||
const talentItems = (slot: number) =>
|
||||
relatedTalents
|
||||
.filter((relation) => Number(relation.talent_slot) === slot)
|
||||
@@ -801,11 +861,11 @@ export const load: PageServerLoad = async ({ params, url, cookies }) => {
|
||||
? managedImageUrl(String(row.render_id), row.render_updated_at)
|
||||
: '',
|
||||
gameIcon: weaponIcon(row.category),
|
||||
headerBadge: `T${row.native_tier}`,
|
||||
headerBadges: [
|
||||
`T${row.native_tier}`,
|
||||
...(Number(row.is_exotic) ? ['EXOTIC'] : []),
|
||||
],
|
||||
headerMetaIcon: weaponIcon(row.damage_type),
|
||||
badges: Number(row.is_exotic)
|
||||
? [{ label: 'EXOTIC', tone: 'exotic' }]
|
||||
: [],
|
||||
factLayout: 'stack' as const,
|
||||
facts: [
|
||||
{
|
||||
@@ -831,7 +891,7 @@ export const load: PageServerLoad = async ({ params, url, cookies }) => {
|
||||
],
|
||||
},
|
||||
],
|
||||
lines: [{ label: 'BASE BONUSES', value: String(row.bonus_count) }],
|
||||
weaponModSlots: modSlots,
|
||||
iconGroups: [
|
||||
{
|
||||
label: 'TALENT 1 POOL',
|
||||
@@ -914,28 +974,28 @@ export const load: PageServerLoad = async ({ params, url, cookies }) => {
|
||||
? managedImageUrl(String(row.render_id), row.render_updated_at)
|
||||
: '',
|
||||
iconUrl: iconUrl('os_protocol', row.id, row.icon_updated_at),
|
||||
badges: [
|
||||
{
|
||||
label: String(row.quality),
|
||||
tone: String(row.quality).toLowerCase().replace('-', ''),
|
||||
},
|
||||
],
|
||||
facts: [
|
||||
{ label: 'LEVELS', value: '01–50' },
|
||||
{
|
||||
label: 'PRIMARY EFFECT',
|
||||
value: row.primary_stat
|
||||
? `${row.primary_stat} · ${valueWithUnit(row.base_value, row.primary_unit)}`
|
||||
: 'NOT CONFIGURED',
|
||||
},
|
||||
{ label: 'TALENT UPGRADES', value: '0–5' },
|
||||
],
|
||||
lines: [
|
||||
{
|
||||
label: 'UNIQUE TALENT',
|
||||
value: list(row.talent_name, 'NOT CONFIGURED'),
|
||||
},
|
||||
],
|
||||
topBorderTone: String(row.quality).toLowerCase().replace('-', ''),
|
||||
headerBadge: String(row.quality).toUpperCase(),
|
||||
headerBadgeTone: String(row.quality).toLowerCase().replace('-', ''),
|
||||
factLayout: 'stack' as const,
|
||||
facts: [],
|
||||
protocolPreview: {
|
||||
primaryStat: String(row.primary_stat ?? 'NOT CONFIGURED'),
|
||||
primaryUnit: String(row.primary_unit ?? ''),
|
||||
primaryBaseValue: Number(row.base_value),
|
||||
talentDescription: row.talent_description
|
||||
? String(row.talent_description)
|
||||
: undefined,
|
||||
talentEffects: protocolTalentEffects
|
||||
.filter((effect) => effect.protocol_id === row.id)
|
||||
.map((effect) => ({
|
||||
baseValue: Number(effect.base_value),
|
||||
unit: String(effect.unit ?? ''),
|
||||
})),
|
||||
},
|
||||
description: row.talent_description
|
||||
? String(row.talent_description)
|
||||
: undefined,
|
||||
};
|
||||
case 'talents':
|
||||
return talentCardItem(row, mechanicsFor(row.id));
|
||||
|
||||
Reference in New Issue
Block a user