diff --git a/src/lib/components/ui/WeaponTypeahead.svelte b/src/lib/components/ui/WeaponTypeahead.svelte
new file mode 100644
index 00000000..73444a4b
--- /dev/null
+++ b/src/lib/components/ui/WeaponTypeahead.svelte
@@ -0,0 +1,364 @@
+
+
+
+
+
+
+
+
+ {#snippet toggleIcon(dropdownShow)}
+
+ {/snippet}
+ {#snippet option(opt)}
+ {@const weapon = opt as WeaponOption}
+
+

+
{weapon.label}
+
+ {/snippet}
+ {#snippet selection(sel)}
+ {@const weapon = (sel as WeaponOption[])[0]}
+ {#if weapon}
+
+

+
{weapon.label}
+
+ {/if}
+ {/snippet}
+
+ {#if isLoading}
+
...
+ {/if}
+
+
+
diff --git a/src/lib/features/database/weapons/sections/WeaponForgeSection.svelte b/src/lib/features/database/weapons/sections/WeaponForgeSection.svelte
new file mode 100644
index 00000000..9712615d
--- /dev/null
+++ b/src/lib/features/database/weapons/sections/WeaponForgeSection.svelte
@@ -0,0 +1,159 @@
+
+
+
+
+{#if hasForgeData}
+
+ {#if editMode}
+
+
+
+
+ {:else}
+ {#if forgeChain.length > 0}
+
+
+
+ {/if}
+
+ {#if forgedFrom && forgeChain.length === 0}
+
+
+ {forgedFrom.name?.en || forgedFrom.name?.ja}
+
+
+ {/if}
+
+ {#if forgeOrder != null}
+
+ {/if}
+ {/if}
+
+{/if}
+
+
diff --git a/src/lib/features/database/weapons/sections/WeaponUncapSection.svelte b/src/lib/features/database/weapons/sections/WeaponUncapSection.svelte
index cde07d2c..48ab085a 100644
--- a/src/lib/features/database/weapons/sections/WeaponUncapSection.svelte
+++ b/src/lib/features/database/weapons/sections/WeaponUncapSection.svelte
@@ -46,6 +46,22 @@
const uncapLevel = $derived(transcendence ? 6 : ulb ? 5 : flb ? 4 : 3)
const transcendenceStage = $derived(transcendence ? 5 : 0)
+ // Extra prerequisite options for dropdown
+ const extraPrerequisiteOptions = [
+ { value: '', label: 'None' },
+ { value: 3, label: 'MLB' },
+ { value: 4, label: 'FLB' },
+ { value: 5, label: 'ULB' },
+ { value: 6, label: 'Transcendence' }
+ ]
+
+ // Get label for extra prerequisite value
+ function getExtraPrerequisiteLabel(value: number | null | undefined): string {
+ if (value == null) return '—'
+ const option = extraPrerequisiteOptions.find((o) => o.value === value)
+ return option?.label ?? '—'
+ }
+
// Get element name for checkbox theming
const elementName = $derived.by((): ElementName | undefined => {
const el = editMode ? editData.element : weapon?.element
@@ -132,5 +148,15 @@
element={elementName}
onchange={handleTranscendenceChange}
/>
+
+ {:else if weapon?.uncap?.extraPrerequisite != null}
+
{/if}
diff --git a/src/lib/types/api/entities.ts b/src/lib/types/api/entities.ts
index 2f2b3a99..dade4861 100644
--- a/src/lib/types/api/entities.ts
+++ b/src/lib/types/api/entities.ts
@@ -42,6 +42,7 @@ export interface Weapon {
flb: boolean
ulb: boolean
transcendence: boolean
+ extraPrerequisite?: number | null
}
transcendenceHp?: number
transcendenceAtk?: number
@@ -57,6 +58,10 @@ export interface Weapon {
kamigame?: string
nicknames?: { en?: string[]; ja?: string[] }
recruits?: string | { id: string; granblueId: string; name: LocalizedName }
+ // Forge chain fields
+ forgeOrder?: number | null
+ forgedFrom?: { id: string; granblueId: string; name: LocalizedName } | null
+ forgeChain?: Array<{ id: string; granblueId: string; name: LocalizedName; forgeOrder: number }> | null
}
// Character entity from CharacterBlueprint
diff --git a/src/routes/(app)/database/weapons/[granblueId]/+page.svelte b/src/routes/(app)/database/weapons/[granblueId]/+page.svelte
index cf515899..c72538c5 100644
--- a/src/routes/(app)/database/weapons/[granblueId]/+page.svelte
+++ b/src/routes/(app)/database/weapons/[granblueId]/+page.svelte
@@ -25,6 +25,7 @@
import WeaponTaxonomySection from '$lib/features/database/weapons/sections/WeaponTaxonomySection.svelte'
import WeaponStatsSection from '$lib/features/database/weapons/sections/WeaponStatsSection.svelte'
import WeaponGachaSection from '$lib/features/database/weapons/sections/WeaponGachaSection.svelte'
+ import WeaponForgeSection from '$lib/features/database/weapons/sections/WeaponForgeSection.svelte'
import EntityImagesTab from '$lib/features/database/detail/tabs/EntityImagesTab.svelte'
import EntityRawDataTab from '$lib/features/database/detail/tabs/EntityRawDataTab.svelte'
import DetailsContainer from '$lib/components/ui/DetailsContainer.svelte'
@@ -205,6 +206,7 @@
+
diff --git a/src/routes/(app)/database/weapons/[granblueId]/edit/+page.svelte b/src/routes/(app)/database/weapons/[granblueId]/edit/+page.svelte
index 4a815587..69036a7f 100644
--- a/src/routes/(app)/database/weapons/[granblueId]/edit/+page.svelte
+++ b/src/routes/(app)/database/weapons/[granblueId]/edit/+page.svelte
@@ -17,6 +17,7 @@
import WeaponTaxonomySection from '$lib/features/database/weapons/sections/WeaponTaxonomySection.svelte'
import WeaponStatsSection from '$lib/features/database/weapons/sections/WeaponStatsSection.svelte'
import WeaponGachaSection from '$lib/features/database/weapons/sections/WeaponGachaSection.svelte'
+ import WeaponForgeSection from '$lib/features/database/weapons/sections/WeaponForgeSection.svelte'
import DetailsContainer from '$lib/components/ui/DetailsContainer.svelte'
import DetailItem from '$lib/components/ui/DetailItem.svelte'
import TagInput from '$lib/components/ui/TagInput.svelte'
@@ -74,6 +75,7 @@
flb: false,
ulb: false,
transcendence: false,
+ extraPrerequisite: '' as number | '',
extra: false,
limit: false,
ax: false,
@@ -88,7 +90,10 @@
kamigame: '',
nicknamesEn: [] as string[],
nicknamesJp: [] as string[],
- recruits: ''
+ recruits: '',
+ // Forge chain fields
+ forgedFrom: '' as string | null,
+ forgeOrder: null as number | null
})
// Populate edit data when weapon loads
@@ -117,6 +122,7 @@
flb: weapon.uncap?.flb || false,
ulb: weapon.uncap?.ulb || false,
transcendence: weapon.uncap?.transcendence || false,
+ extraPrerequisite: weapon.uncap?.extraPrerequisite ?? '',
extra: weapon.extra || false,
limit: Boolean(weapon.limit),
ax: weapon.ax || false,
@@ -131,7 +137,10 @@
kamigame: weapon.kamigame || '',
nicknamesEn: weapon.nicknames?.en || [],
nicknamesJp: weapon.nicknames?.ja || [],
- recruits: typeof weapon.recruits === 'string' ? weapon.recruits : (weapon.recruits?.granblueId ?? '')
+ recruits: typeof weapon.recruits === 'string' ? weapon.recruits : (weapon.recruits?.granblueId ?? ''),
+ // Forge chain fields
+ forgedFrom: weapon.forgedFrom?.granblueId || null,
+ forgeOrder: weapon.forgeOrder ?? null
}
}
})
@@ -165,6 +174,7 @@
flb: editData.flb,
ulb: editData.ulb,
transcendence: editData.transcendence,
+ extra_prerequisite: editData.extraPrerequisite === '' ? null : editData.extraPrerequisite,
extra: editData.extra,
limit: editData.limit,
ax: editData.ax,
@@ -179,7 +189,10 @@
kamigame: editData.kamigame,
nicknames_en: editData.nicknamesEn,
nicknames_jp: editData.nicknamesJp,
- recruits: editData.recruits || undefined
+ recruits: editData.recruits || undefined,
+ // Forge chain fields
+ forged_from: editData.forgedFrom || null,
+ forge_order: editData.forgeOrder
}
await entityAdapter.updateWeapon(weapon.id, payload)
@@ -226,6 +239,7 @@
+
diff --git a/src/routes/(app)/database/weapons/import/+page.svelte b/src/routes/(app)/database/weapons/import/+page.svelte
index ffbc1524..23c8fa99 100644
--- a/src/routes/(app)/database/weapons/import/+page.svelte
+++ b/src/routes/(app)/database/weapons/import/+page.svelte
@@ -20,6 +20,7 @@
import WeaponStatsSection from '$lib/features/database/weapons/sections/WeaponStatsSection.svelte'
import WeaponMetadataSection from '$lib/features/database/weapons/sections/WeaponMetadataSection.svelte'
import WeaponGachaSection from '$lib/features/database/weapons/sections/WeaponGachaSection.svelte'
+ import WeaponForgeSection from '$lib/features/database/weapons/sections/WeaponForgeSection.svelte'
import TabbedEntitySelector from '$lib/features/database/import/TabbedEntitySelector.svelte'
import type { EntityTab } from '$lib/features/database/import/TabbedEntitySelector.svelte'
import DetailsContainer from '$lib/components/ui/DetailsContainer.svelte'
@@ -123,6 +124,7 @@
flb: suggestions?.flb ?? false,
ulb: suggestions?.ulb ?? false,
transcendence: suggestions?.transcendence ?? false,
+ extraPrerequisite: '' as number | '',
extra: false,
limit: false,
ax: false,
@@ -136,7 +138,10 @@
kamigame: suggestions?.kamigame ?? '',
nicknamesEn: [] as string[],
nicknamesJp: [] as string[],
- recruits: suggestions?.recruits ?? null
+ recruits: suggestions?.recruits ?? null,
+ // Forge chain
+ forgedFrom: null as string | null,
+ forgeOrder: null as number | null
}
}
@@ -279,6 +284,7 @@
flb: formData.flb,
ulb: formData.ulb,
transcendence: formData.transcendence,
+ extra_prerequisite: formData.extraPrerequisite === '' ? null : formData.extraPrerequisite,
extra: formData.extra,
limit: formData.limit,
ax: formData.ax,
@@ -293,7 +299,10 @@
nicknames_en: formData.nicknamesEn,
nicknames_jp: formData.nicknamesJp,
recruits: formData.recruits,
- wiki_raw: wikiRawByPage[selectedWikiPage] || undefined
+ wiki_raw: wikiRawByPage[selectedWikiPage] || undefined,
+ // Forge chain
+ forged_from: formData.forgedFrom || null,
+ forge_order: formData.forgeOrder
}
const newWeapon = await entityAdapter.createWeapon(payload)
@@ -510,6 +519,12 @@
onDismissSuggestion={handleDismissSuggestion}
/>
+
+
diff --git a/src/routes/(app)/database/weapons/new/+page.svelte b/src/routes/(app)/database/weapons/new/+page.svelte
index 85b7a4b8..53bf4625 100644
--- a/src/routes/(app)/database/weapons/new/+page.svelte
+++ b/src/routes/(app)/database/weapons/new/+page.svelte
@@ -12,6 +12,7 @@
import WeaponUncapSection from '$lib/features/database/weapons/sections/WeaponUncapSection.svelte'
import WeaponTaxonomySection from '$lib/features/database/weapons/sections/WeaponTaxonomySection.svelte'
import WeaponStatsSection from '$lib/features/database/weapons/sections/WeaponStatsSection.svelte'
+ import WeaponForgeSection from '$lib/features/database/weapons/sections/WeaponForgeSection.svelte'
import DetailsContainer from '$lib/components/ui/DetailsContainer.svelte'
import DetailItem from '$lib/components/ui/DetailItem.svelte'
import SidebarHeader from '$lib/components/ui/SidebarHeader.svelte'
@@ -79,6 +80,7 @@
flb: false,
ulb: false,
transcendence: false,
+ extraPrerequisite: '' as number | '',
extra: false,
limit: false,
ax: false,
@@ -100,7 +102,11 @@
nicknamesJp: [] as string[],
// Recruits (Character ID)
- recruits: null as string | null
+ recruits: null as string | null,
+
+ // Forge chain
+ forgedFrom: null as string | null,
+ forgeOrder: null as number | null
})
const rarityOptions = getRarityOptions()
@@ -183,6 +189,7 @@
flb: editData.flb,
ulb: editData.ulb,
transcendence: editData.transcendence,
+ extra_prerequisite: editData.extraPrerequisite === '' ? null : editData.extraPrerequisite,
extra: editData.extra,
limit: editData.limit,
ax: editData.ax,
@@ -204,7 +211,11 @@
nicknames_jp: editData.nicknamesJp,
// Recruits
- recruits: editData.recruits
+ recruits: editData.recruits,
+
+ // Forge chain
+ forged_from: editData.forgedFrom || null,
+ forge_order: editData.forgeOrder
}
const newWeapon = await entityAdapter.createWeapon(payload)
@@ -285,6 +296,7 @@
+