From 0e52650986b2a0d1de285192169e6b963986544d Mon Sep 17 00:00:00 2001 From: wizjany Date: Thu, 9 Jul 2026 21:38:23 -0400 Subject: [PATCH 1/8] Show claim research on item pages. Also make research display better. --- frontend/src/lib/relations.ts | 7 ++++ .../lib/table-utils/detail-tab-builders.tsx | 33 ++++++++++++++++++- frontend/src/routes/database/cargo/[id].tsx | 4 +++ .../routes/database/claim-research/[id].tsx | 6 ++-- frontend/src/routes/database/item/[id].tsx | 4 +++ 5 files changed, 50 insertions(+), 4 deletions(-) diff --git a/frontend/src/lib/relations.ts b/frontend/src/lib/relations.ts index dd7168a..dba6978 100644 --- a/frontend/src/lib/relations.ts +++ b/frontend/src/lib/relations.ts @@ -11,6 +11,7 @@ import {createMemo} from "solid-js"; import {AchievementDesc} from "~/bindings/src/achievement_desc_type"; import {BuildingDesc} from "~/bindings/src/building_desc_type"; +import {ClaimTechDesc} from "~/bindings/src/claim_tech_desc_type"; import {CollectibleDesc} from "~/bindings/src/collectible_desc_type"; import {ConstructionRecipeDesc} from "~/bindings/src/construction_recipe_desc_type"; import {ContributionLootDesc} from "~/bindings/src/contribution_loot_desc_type"; @@ -316,6 +317,12 @@ export function resourcesYieldingResource(res: number): ResourceDesc[] { // ─── Cross-Table Relationships ────────────────────────────────── +export function claimResearchRequiring(itemId: number, itemType: string): ClaimTechDesc[] { + const all = BitCraftTables.ClaimTechDesc.get(); + if (!all) return []; + return all.filter(t => anyStackMatches(t.input, itemId, itemType)); +} + /** Enemies associated with a resource (via enemyParamsId -> EnemyAiParamsDesc -> EnemyDesc) */ export function enemiesForResource(resource: ResourceDesc): EnemyDesc[] { if (!resource.enemyParamsId?.length) return []; diff --git a/frontend/src/lib/table-utils/detail-tab-builders.tsx b/frontend/src/lib/table-utils/detail-tab-builders.tsx index 33a8f99..87bd72d 100644 --- a/frontend/src/lib/table-utils/detail-tab-builders.tsx +++ b/frontend/src/lib/table-utils/detail-tab-builders.tsx @@ -7,6 +7,7 @@ import {A} from "@solidjs/router"; import {createSignal, Show} from "solid-js"; +import {ClaimTechDesc} from "~/bindings/src/claim_tech_desc_type"; import {CollectibleDesc} from "~/bindings/src/collectible_desc_type"; import {ConstructionRecipeDesc} from "~/bindings/src/construction_recipe_desc_type"; import {CraftingRecipeDesc} from "~/bindings/src/crafting_recipe_desc_type"; @@ -41,7 +42,7 @@ import { TravelerTradePanel, } from "~/components/shared/RecipeDisplay"; import {Button} from "~/components/ui/button"; -import {CollectibleLink, QuestChainLink} from "~/lib/game-links"; +import {CollectibleLink, IconLink, IconSpan, ItemStackLink, LinkedList, pageIcon, QuestChainLink} from "~/lib/game-links"; import {getInteractionName, getPlacementName} from "~/lib/placeables"; import { getConstructionRecipeName, @@ -407,6 +408,36 @@ export function collectiblesTab(collectibles: CollectibleDesc[]) : RelationshipT ), } } + +export function claimResearchTab(techs: ClaimTechDesc[]) : RelationshipTab { + return { + id: "claim-tech", + label: "Claim Research", + count: techs.length, + showWhenEmpty: false, + content: () => ( + + data={techs} + columns={[ + {header: "Claim Research", cell: c => ( + + {c.name} + + )}, + {header: "Required", cell: c => ( + + { + c.input.map(is => ) + .concat(c.suppliesCost > 0 ? [Supplies ×{c.suppliesCost}] : []) + } + + )} + ]} + /> + ), + } +} + // ─── Single Recipe Tab Builders (for building page) ──────────── export function constructionCombinedSingleTab( diff --git a/frontend/src/routes/database/cargo/[id].tsx b/frontend/src/routes/database/cargo/[id].tsx index 41c5ccb..1a67453 100644 --- a/frontend/src/routes/database/cargo/[id].tsx +++ b/frontend/src/routes/database/cargo/[id].tsx @@ -7,6 +7,7 @@ import {CargoIcon} from "~/components/shared/GameIcon"; import {breadcrumb} from "~/lib/game-links"; import {interactionsInvolvingItem, placementsConsumingItem} from "~/lib/placeables"; import { + claimResearchRequiring, constructionRecipesConsuming, conversionRecipesConsuming, conversionRecipesProducing, @@ -27,6 +28,7 @@ import { import {useSettings} from "~/lib/settings"; import {BitCraftTables, useTablesLoading} from "~/lib/spacetime"; import { + claimResearchTab, constructionCombinedTab, conversionTab, craftedFromTab, @@ -83,6 +85,7 @@ export default function CargoDetail() { const tradeRequires = createMemo(() => cargoId() != null ? travelerTradesRequiring(cargoId()!, cargoType) : []); const tradeOffers = createMemo(() => cargoId() != null ? travelerTradesOffering(cargoId()!, cargoType) : []); const depletionSources = createMemo(() => cargoId() != null ? resourcesYielding(cargoId()!, cargoType) : []); + const researchRequires = createMemo(() => cargoId() != null ? claimResearchRequiring(cargoId()!, cargoType) : []); const {extractionDrops, enemyDrops, inItemLists} = questDropAugmentedLists(cargoId, cargoType); const questRequires = createMemo(() => { @@ -141,6 +144,7 @@ export default function CargoDetail() { questRewardsTab(questRewards()), placeablePlacementTab(placeablePlacements()), placeableInteractionsTab(placeableInteractions()), + claimResearchTab(researchRequires()), ...(cargo()?.id === 92169812 && easterEggs() ? [lootTab()] : []) ]} /> diff --git a/frontend/src/routes/database/claim-research/[id].tsx b/frontend/src/routes/database/claim-research/[id].tsx index 385c561..eec769f 100644 --- a/frontend/src/routes/database/claim-research/[id].tsx +++ b/frontend/src/routes/database/claim-research/[id].tsx @@ -2,8 +2,7 @@ import {useNavigate, useParams} from "@solidjs/router"; import {createMemo} from "solid-js"; import {ClaimTechDesc} from "~/bindings/src/claim_tech_desc_type"; import {DetailPageLayout, RelTable} from "~/components/shared/DetailPageLayout"; -import {ItemStackArray} from "~/components/shared/ItemStacks"; -import {breadcrumb} from "~/lib/game-links"; +import {breadcrumb, ItemStackLink, LinkedList} from "~/lib/game-links"; import {BitCraftTables, useTablesLoading} from "~/lib/spacetime"; import {readableSeconds, undefinedIfZero} from "~/lib/utils"; @@ -52,7 +51,7 @@ export default function ClaimResearchDetail() { { heading: "Cost", properties: [ - {label: "Item Cost", value: tech()?.input.length ? : undefined}, + {label: "Item Cost", value: tech()?.input.length ? {tech()!.input.map(is => )} : "None"}, {label: "Supply Cost", value: tech()?.suppliesCost}, {label: "Research Time", value: readableSeconds(undefinedIfZero(tech()?.researchTime))}, ] @@ -81,6 +80,7 @@ export default function ClaimResearchDetail() { id: "unlocks", label: "Unlocks Techs", count: unlocksTechs().length, + showWhenEmpty: false, content: () => data={unlocksTechs()} columns={claimTechColumns} onRowClick={(row) => navigate(`/database/claim-research/${row.id}`)}/> }, ]} diff --git a/frontend/src/routes/database/item/[id].tsx b/frontend/src/routes/database/item/[id].tsx index dabf8db..8f392f6 100644 --- a/frontend/src/routes/database/item/[id].tsx +++ b/frontend/src/routes/database/item/[id].tsx @@ -8,6 +8,7 @@ import {Tooltip, TooltipContent, TooltipTrigger} from "~/components/ui/tooltip"; import {breadcrumb, IconLink, pageIcon, SkillLinkById} from "~/lib/game-links"; import {interactionsInvolvingItem, placementsConsumingItem} from "~/lib/placeables"; import { + claimResearchRequiring, constructionRecipesConsuming, conversionRecipesConsuming, conversionRecipesProducing, @@ -33,6 +34,7 @@ import {useSettings} from "~/lib/settings"; import {BitCraftTables, useTablesLoading} from "~/lib/spacetime"; import {buffsGroups} from "~/lib/table-utils/detail-group-builders"; import { + claimResearchTab, collectiblesTab, constructionCombinedTab, conversionTab, @@ -145,6 +147,7 @@ export default function ItemDetail() { const tradeRequires = createMemo(() => itemId() != null ? travelerTradesRequiring(itemId()!, itemType) : []); const tradeOffers = createMemo(() => itemId() != null ? travelerTradesOffering(itemId()!, itemType) : []); const depletionSources = createMemo(() => itemId() != null ? resourcesYielding(itemId()!, itemType) : []); + const researchRequires = createMemo(() => itemId() != null ? claimResearchRequiring(itemId()!, itemType) : []); const isItemList = createMemo(() => item() ? item()?.itemListId ? BitCraftTables.ItemListDesc.indexedBy("id")().get(item()?.itemListId) : undefined : undefined); const {extractionDrops, enemyDrops, inItemLists} = questDropAugmentedLists(itemId, itemType); @@ -320,6 +323,7 @@ export default function ItemDetail() { questRewardsTab(questRewards()), placeablePlacementTab(placeablePlacements()), placeableInteractionsTab(placeableInteractions()), + claimResearchTab(researchRequires()), ...(item()?.id === 164053808 && easterEggs() ? [lootTabWith({loot: [[1602206011, "Item", 1687372047]], chest: [item()!.iconAssetName, item()!.rarity, item()!.tier]})] : []) ]} /> From 07545443b0c3e2dafb7043f528f4c0834d44d1ec Mon Sep 17 00:00:00 2001 From: wizjany Date: Fri, 10 Jul 2026 08:51:28 -0400 Subject: [PATCH 2/8] Add some prospecting details. --- frontend/src/lib/utils.ts | 1 + frontend/src/routes/database/combat/[id].tsx | 4 ++- .../src/routes/database/prospecting/[id].tsx | 27 ++++++++++--------- frontend/src/routes/database/skill/[id].tsx | 4 ++- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/frontend/src/lib/utils.ts b/frontend/src/lib/utils.ts index 4b2713d..43d73b9 100644 --- a/frontend/src/lib/utils.ts +++ b/frontend/src/lib/utils.ts @@ -66,6 +66,7 @@ export function undefinedIfZero(val: number | undefined) { export function readableSeconds(seconds: number | undefined, shorten: boolean = false): string | undefined { if (seconds === undefined) return undefined; + if (seconds === -1) return "-1"; seconds = Math.round(seconds); const hours = Math.floor(seconds / 3600) const minutes = Math.floor((seconds % 3600) / 60) diff --git a/frontend/src/routes/database/combat/[id].tsx b/frontend/src/routes/database/combat/[id].tsx index ac54519..3a9d418 100644 --- a/frontend/src/routes/database/combat/[id].tsx +++ b/frontend/src/routes/database/combat/[id].tsx @@ -1,7 +1,8 @@ import {useParams} from "@solidjs/router"; -import {createMemo} from "solid-js"; +import {createMemo, Show} from "solid-js"; import {BuffEffect} from "~/bindings/src/buff_effect_type"; import {WeaponTypeDesc} from "~/bindings/src/weapon_type_desc_type"; +import {FontIcon} from "~/components/icons/font-icons"; import {DetailPageLayout} from "~/components/shared/DetailPageLayout"; import {BuffTable} from "~/components/shared/RelTablePresets"; import {breadcrumb} from "~/lib/game-links"; @@ -60,6 +61,7 @@ export default function CombatDetail() { breadcrumb={breadcrumb("/database/combat", "Combat Ability")} loading={isLoading() && !action()} name={action()?.name ?? "Combat action not found"} + icon={{c => }} description={action()?.description} details={[ { diff --git a/frontend/src/routes/database/prospecting/[id].tsx b/frontend/src/routes/database/prospecting/[id].tsx index f428afb..ae8ae5d 100644 --- a/frontend/src/routes/database/prospecting/[id].tsx +++ b/frontend/src/routes/database/prospecting/[id].tsx @@ -1,10 +1,11 @@ import {useParams} from "@solidjs/router"; -import {createMemo, For} from "solid-js"; +import {createMemo, For, Show} from "solid-js"; import {ProspectingDesc} from "~/bindings/src/prospecting_desc_type"; +import {FontIcon} from "~/components/icons/font-icons"; import {DetailPageLayout, RelTable} from "~/components/shared/DetailPageLayout"; import {EnemyIcon, ResourceIcon} from "~/components/shared/GameIcon"; import {ItemStackArray} from "~/components/shared/ItemStacks"; -import {BiomeLink, breadcrumb} from "~/lib/game-links"; +import {BiomeLink, breadcrumb, SkillLinkById} from "~/lib/game-links"; import {BitCraftTables, useTablesLoading} from "~/lib/spacetime"; import {fixFloat, readableSeconds} from "~/lib/utils"; @@ -48,7 +49,6 @@ export default function ProspectingDetail() { const isLoading = useTablesLoading(BitCraftTables.ProspectingDesc); const index = BitCraftTables.ProspectingDesc.indexedBy("id"); const biomeIndex = BitCraftTables.BiomeDesc.indexedBy("biomeType"); - const skillIndex = BitCraftTables.SkillDesc.indexedBy("id"); const enemyIndex = BitCraftTables.EnemyDesc.indexedBy("enemyType"); const enemyParamsIndex = BitCraftTables.EnemyAiParamsDesc.indexedBy("id"); @@ -58,12 +58,11 @@ export default function ProspectingDetail() { return index()?.get(id); }); - const breadCrumbCount = createMemo(() => { - const arr = prospecting()?.breadCrumbCount; - if (!arr || arr.length === 0) return "—"; - if (arr.length >= 2 && arr[0] !== arr[arr.length - 1]) return `${prospecting()!.breadCrumbCount[0]}–${prospecting()!.breadCrumbCount[prospecting()!.breadCrumbCount.length - 1]}`; + const rangeStr = (arr: Array | undefined) => { + if (!arr || arr.length === 0) return undefined; + if (arr.length >= 2 && arr[0] !== arr[arr.length - 1]) return `${arr[0]}–${arr[arr.length - 1]}`; return arr[0]; - }); + } const biomes = createMemo(() => { const p = prospecting(); @@ -77,8 +76,7 @@ export default function ProspectingDetail() { const p = prospecting(); if (!p?.experiencePerNode) return undefined; if (!p.experiencePerNode.quantity) return undefined; - const name = skillIndex()?.get(p.experiencePerNode.skillId)?.name ?? `Skill ${p.experiencePerNode.skillId}`; - return `${name}: ${fixFloat(p.experiencePerNode.quantity)}`; + return <>: ${fixFloat(p.experiencePerNode.quantity)}`; }); const spawnInfo = createMemo(() => { @@ -109,15 +107,18 @@ export default function ProspectingDetail() { breadcrumb={breadcrumb("/database/prospecting")} loading={isLoading() && !prospecting()} name={prospecting()?.name ?? "Prospecting entry not found"} + icon={{c => }} description={prospecting()?.description} details={[ - {label: "Breadcrumb Count", value: breadCrumbCount()}, + {label: "Breadcrumb Count", value: rangeStr(prospecting()?.breadCrumbCount)}, {label: "Contribution Per Crumb", value: prospecting()?.contributionPerVisitedBreadCrumb}, {label: "% Nodes for Max Contribution", value: (prospecting()?.pctNodesForMaxContribution ?? 0) * 100}, {label: "Single Contribution Only", value: prospecting()?.singleContributionOnly}, - {label: "Experience Per Node", value: experienceStr()}, + {label: "Breadcrumb Distance", value: rangeStr(prospecting()?.distanceBetweenBreadCrumbs)}, + {label: "Breadcrumb Radius", value: rangeStr(prospecting()?.breadCrumbRadius)}, {label: "Deadzone Angle", value: prospecting()?.deadzoneAngleBetweenCrumbs}, - {label: "Join Radius", value: prospecting()!.joinRadius}, + {label: "Join Radius", value: prospecting()?.joinRadius}, + {label: "Experience Per Node", value: experienceStr()}, {label: "Pointer Duration", value: readableSeconds(prospecting()?.pointerDuration)}, {label: "Prospecting Duration", value: readableSeconds(prospecting()?.prospectingDuration)}, {label: "Is Aquatic Resource", value: prospecting()?.isAquaticResource}, diff --git a/frontend/src/routes/database/skill/[id].tsx b/frontend/src/routes/database/skill/[id].tsx index 048e589..d3abc95 100644 --- a/frontend/src/routes/database/skill/[id].tsx +++ b/frontend/src/routes/database/skill/[id].tsx @@ -1,5 +1,6 @@ import {useParams} from "@solidjs/router"; -import {createMemo} from "solid-js"; +import {createMemo, Show} from "solid-js"; +import {FontIcon} from "~/components/icons/font-icons"; import {DetailPageLayout} from "~/components/shared/DetailPageLayout"; import {breadcrumb} from "~/lib/game-links"; import {BitCraftTables, useTablesLoading} from "~/lib/spacetime"; @@ -27,6 +28,7 @@ export default function SkillDetail() { breadcrumb={breadcrumb("/database/skill", skillTag())} loading={isLoading() && !skill()} name={skill()?.name ?? `Skill #${params.id}`} + icon={{c => }} description={skill()?.description} tag={skillTag()} details={[ From 32699191a035d5c857640b53cbf90827f219ef97 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 10 Jul 2026 13:32:53 +0000 Subject: [PATCH 3/8] chore: update submodules to latest versions --- frontend/public/bsatn | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/bsatn b/frontend/public/bsatn index d811651..5a4bd1c 160000 --- a/frontend/public/bsatn +++ b/frontend/public/bsatn @@ -1 +1 @@ -Subproject commit d811651334ed6dad50ae1f7798b6aa2dfbefde54 +Subproject commit 5a4bd1c9d72e3156afe68abd8efde3e4a6f503b0 From 54bff7877967740b777c60e307bd97ba5ea32a81 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 10 Jul 2026 19:58:44 +0000 Subject: [PATCH 4/8] chore: update submodules to latest versions --- frontend/public/bsatn | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/bsatn b/frontend/public/bsatn index 5a4bd1c..3b26e4d 160000 --- a/frontend/public/bsatn +++ b/frontend/public/bsatn @@ -1 +1 @@ -Subproject commit 5a4bd1c9d72e3156afe68abd8efde3e4a6f503b0 +Subproject commit 3b26e4d7f52c3e5f171ef487cd1130749f59cac3 From a09c8d2b28ee44feb07699d8fededd1e64646e36 Mon Sep 17 00:00:00 2001 From: wizjany Date: Sun, 12 Jul 2026 14:13:02 -0400 Subject: [PATCH 5/8] Better search result UX. --- frontend/src/components/GlobalSearchInput.tsx | 52 ++++++++++--------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/frontend/src/components/GlobalSearchInput.tsx b/frontend/src/components/GlobalSearchInput.tsx index 778458b..9a465e1 100644 --- a/frontend/src/components/GlobalSearchInput.tsx +++ b/frontend/src/components/GlobalSearchInput.tsx @@ -10,7 +10,7 @@ * - Enter navigates to full /search page */ -import {useNavigate} from "@solidjs/router"; +import {A, useNavigate} from "@solidjs/router"; import {TbOutlineSearch as IconSearch} from "solid-icons/tb"; import {createMemo, createSignal, For, onCleanup, onMount, Show} from "solid-js"; import {useIsMobile} from "~/components/ui/sidebar"; @@ -97,11 +97,15 @@ export default function GlobalSearchInput(props: GlobalSearchInputProps) { tabButtonRefs.get(tabId)?.scrollIntoView({block: "nearest", inline: "nearest"}); }; - const commitSuggestion = (match: ObjectMatch) => { + const closeSuggestions = () => { setQuery(""); setDropdownOpen(false); setActiveIndex(-1); if (inputRef) inputRef.value = ""; + }; + + const commitSuggestion = (match: ObjectMatch) => { + closeSuggestions(); navigate(match.route); }; @@ -260,30 +264,28 @@ export default function GlobalSearchInput(props: GlobalSearchInputProps) { {(match, i) => { const isActive = () => activeIndex() === i(); return ( -
  • { - e.preventDefault(); - commitSuggestion(match); - }} - onMouseEnter={() => setActiveIndex(i())} - > - - {match.label} - - - {match.displayName} - - - ({match.matchField}: {match.matchValue.length > 50 ? match.matchValue.slice(0, 50) + "…" : match.matchValue}) - +
  • + setActiveIndex(i())} + > + + {match.label} - + + {match.displayName} + + + ({match.matchField}: {match.matchValue.length > 50 ? match.matchValue.slice(0, 50) + "…" : match.matchValue}) + + + +
  • ); }} From b1427c5f30baf6f137427882db662b47aa572ee5 Mon Sep 17 00:00:00 2001 From: wizjany Date: Tue, 14 Jul 2026 12:16:56 -0400 Subject: [PATCH 6/8] Don't question table indices. First versions of brico v1 had nullable indices. No more. --- frontend/.gitignore | 7 +-- frontend/src/components/fun/BricoLootBox.tsx | 10 ++-- frontend/src/components/shared/GameIcon.tsx | 2 +- frontend/src/components/shared/ItemStacks.tsx | 28 +++++----- .../src/components/shared/PlaceableGraph.tsx | 22 ++++---- .../src/components/shared/RecipeDisplay.tsx | 8 +-- frontend/src/lib/game-links.tsx | 18 +++---- frontend/src/lib/placeables.ts | 2 +- frontend/src/lib/recipe-sources.tsx | 18 +++---- frontend/src/lib/relations.ts | 51 +++++++++---------- frontend/src/lib/spacetime.ts | 24 ++++----- .../src/lib/table-defs/achievement-table.tsx | 4 +- frontend/src/lib/table-defs/buffs-table.tsx | 2 +- .../src/lib/table-defs/buildings-table.tsx | 4 +- frontend/src/lib/table-defs/cargo-table.tsx | 2 +- .../src/lib/table-defs/collectible-table.tsx | 3 +- frontend/src/lib/table-defs/combat-table.tsx | 4 +- .../src/lib/table-defs/deployables-table.tsx | 12 ++--- .../src/lib/table-defs/equipment-table.tsx | 10 ++-- frontend/src/lib/table-defs/food-table.tsx | 22 ++++---- .../src/lib/table-defs/item-list-table.tsx | 4 +- frontend/src/lib/table-defs/item-table.tsx | 12 ++--- .../src/lib/table-defs/knowledge-table.tsx | 16 +++--- .../src/lib/table-defs/prospecting-table.tsx | 8 +-- frontend/src/lib/table-defs/quests-table.tsx | 20 ++++---- frontend/src/lib/table-defs/tools-table.tsx | 10 ++-- .../lib/table-defs/traveler-tasks-table.tsx | 2 +- .../lib/table-defs/traveler-trades-table.tsx | 2 +- frontend/src/lib/table-defs/weapon-table.tsx | 8 +-- .../src/lib/table-utils/column-builders.tsx | 2 +- .../src/routes/database/achievement/[id].tsx | 4 +- frontend/src/routes/database/biome/[id].tsx | 9 ++-- frontend/src/routes/database/buff/[id].tsx | 10 ++-- .../src/routes/database/building/[id].tsx | 8 +-- frontend/src/routes/database/cargo/[id].tsx | 6 +-- .../routes/database/claim-research/[id].tsx | 6 +-- .../src/routes/database/collectible/[id].tsx | 12 ++--- frontend/src/routes/database/combat/[id].tsx | 6 +-- .../src/routes/database/creature/[id].tsx | 7 ++- .../src/routes/database/deployable/[id].tsx | 13 +++-- .../src/routes/database/equipment/[id].tsx | 6 +-- frontend/src/routes/database/food/[id].tsx | 10 ++-- .../src/routes/database/item-list/[id].tsx | 4 +- frontend/src/routes/database/item/[id].tsx | 22 ++++---- .../src/routes/database/knowledge/[id].tsx | 10 ++-- frontend/src/routes/database/paving/[id].tsx | 7 ++- .../src/routes/database/placeable/[id].tsx | 4 +- .../src/routes/database/prospecting/[id].tsx | 11 ++-- .../src/routes/database/quest-chain/[id].tsx | 5 +- .../src/routes/database/resource/[id].tsx | 4 +- frontend/src/routes/database/skill/[id].tsx | 4 +- .../src/routes/database/terraforming/[id].tsx | 4 +- frontend/src/routes/database/tool/[id].tsx | 8 +-- .../routes/database/traveler-task/[id].tsx | 6 +-- .../routes/database/traveler-trade/[id].tsx | 6 +-- frontend/src/routes/database/weapon/[id].tsx | 8 +-- frontend/src/routes/tools/placeable-graph.tsx | 2 +- 57 files changed, 262 insertions(+), 277 deletions(-) diff --git a/frontend/.gitignore b/frontend/.gitignore index 087eed2..06eb044 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -20,9 +20,10 @@ app.config.timestamp_*.js *.launch .settings/ -# Temp -gitignore - # System Files .DS_Store Thumbs.db + +# rasterized svgs +public/og-icons +public/brico-face.png \ No newline at end of file diff --git a/frontend/src/components/fun/BricoLootBox.tsx b/frontend/src/components/fun/BricoLootBox.tsx index 1f8173a..029325e 100644 --- a/frontend/src/components/fun/BricoLootBox.tsx +++ b/frontend/src/components/fun/BricoLootBox.tsx @@ -32,10 +32,10 @@ function rollItemList(list: ItemListDesc): LootResult | null { if (r <= 0 && poss.items.length > 0) { const stack = poss.items[Math.floor(Math.random() * poss.items.length)]; if (stack.itemType.tag === ItemType.Cargo.tag) { - const cargo = cargoIdx?.get(stack.itemId); + const cargo = cargoIdx.get(stack.itemId); return cargo ? { type: "cargo", cargo, quantity: stack.quantity } : null; } - const item = itemIdx?.get(stack.itemId); + const item = itemIdx.get(stack.itemId); return item ? { type: "item", item, quantity: stack.quantity } : null; } } @@ -159,9 +159,9 @@ export const BricoLootBox: Component = (props) => { setResolvedItemId([targetItemId, targetItemType]); if (targetListId) { - const list = BitCraftTables.ItemListDesc.indexedBy("id")()?.get(targetListId); + const list = BitCraftTables.ItemListDesc.indexedBy("id")().get(targetListId); if (list) { - const item = BitCraftTables.ItemDesc.indexedBy("id")()?.get(targetItemId); + const item = BitCraftTables.ItemDesc.indexedBy("id")().get(targetItemId); const result = rollItemList(list); if (result && item?.itemListId === targetListId) { // input was item list itself, just resolve to the rolled item @@ -268,7 +268,7 @@ export const BricoLootBox: Component = (props) => { const cargo = BitCraftTables.CargoDesc.indexedBy("id")().get(id); return cargo ? {type: "cargo", cargo, quantity: 1} : null; } else { - const item = BitCraftTables.ItemDesc.indexedBy("id")()?.get(id); + const item = BitCraftTables.ItemDesc.indexedBy("id")().get(id); return item ? {type: "item", item, quantity: 1} : null; } }; diff --git a/frontend/src/components/shared/GameIcon.tsx b/frontend/src/components/shared/GameIcon.tsx index 5071db6..305b64b 100644 --- a/frontend/src/components/shared/GameIcon.tsx +++ b/frontend/src/components/shared/GameIcon.tsx @@ -297,7 +297,7 @@ export const ItemIcon: Component = (props) => { const { tf2Mode } = useSettings(); const hatIcon = () => { if (tf2Mode()) { - const eq = BitCraftTables.EquipmentDesc.indexedBy("itemId")?.()?.get(props.item.id); + const eq = BitCraftTables.EquipmentDesc.indexedBy("itemId")().get(props.item.id); if (eq && eq.slots.some(s => s.tag === "HeadClothing")) { return `/assets/Hats/${HATS[Math.floor(Math.random() * HATS.length)]}.webp`; } diff --git a/frontend/src/components/shared/ItemStacks.tsx b/frontend/src/components/shared/ItemStacks.tsx index 25f8bb4..d0fab73 100644 --- a/frontend/src/components/shared/ItemStacks.tsx +++ b/frontend/src/components/shared/ItemStacks.tsx @@ -34,9 +34,9 @@ import {cn, fixFloat} from "~/lib/utils"; /** Resolve an ItemStack's itemId + itemType to the actual ItemDesc or CargoDesc */ function resolveStack(itemId: number, itemType: ItemType): ItemDesc | CargoDesc | undefined { if (itemType.tag === ItemType.Item.tag) { - return BitCraftTables.ItemDesc.indexedBy("id")?.()?.get(itemId); + return BitCraftTables.ItemDesc.indexedBy("id")().get(itemId); } else if (itemType.tag === ItemType.Cargo.tag) { - return BitCraftTables.CargoDesc.indexedBy("id")?.()?.get(itemId); + return BitCraftTables.CargoDesc.indexedBy("id")().get(itemId); } return undefined; } @@ -159,7 +159,7 @@ export const ProbItemStackIcon: Component<{ if (!obj || !isItem(obj)) return undefined; const listId = (obj as ItemDesc).itemListId; if (!listId) return undefined; - return BitCraftTables.ItemListDesc.indexedBy("id")?.()?.get(listId); + return BitCraftTables.ItemListDesc.indexedBy("id")().get(listId); }); return ( @@ -239,8 +239,8 @@ function computeAveragesFlatExpanded(possibilities: ItemListPossibility[], multi const totalWeight = possibilities.reduce((sum, p) => sum + p.probability, 0); if (totalWeight === 0) return []; - const itemIndex = BitCraftTables.ItemDesc.indexedBy("id")?.(); - const listIndex = BitCraftTables.ItemListDesc.indexedBy("id")?.(); + const itemIndex = BitCraftTables.ItemDesc.indexedBy("id")(); + const listIndex = BitCraftTables.ItemListDesc.indexedBy("id")(); for (const poss of possibilities) { const selectProb = (poss.probability / totalWeight) * multiplier; @@ -248,9 +248,9 @@ function computeAveragesFlatExpanded(possibilities: ItemListPossibility[], multi // Check if this stack resolves to an inner item list let handledAsInnerList = false; if (stack.itemType.tag === ItemType.Item.tag) { - const item = itemIndex?.get(stack.itemId); + const item = itemIndex.get(stack.itemId); if (item?.itemListId) { - const innerList = listIndex?.get(item.itemListId); + const innerList = listIndex.get(item.itemListId); if (innerList) { // Recurse: multiply probability by select chance × stack quantity const innerAvgs = computeAveragesFlatExpanded(innerList.possibilities, selectProb * stack.quantity); @@ -301,14 +301,14 @@ export const ItemListDisplay: Component<{ /** True if any possibility contains an item that resolves to an inner item list */ const hasInnerLists = createMemo(() => { - const itemIndex = BitCraftTables.ItemDesc.indexedBy("id")?.(); - const listIndex = BitCraftTables.ItemListDesc.indexedBy("id")?.(); + const itemIndex = BitCraftTables.ItemDesc.indexedBy("id")(); + const listIndex = BitCraftTables.ItemListDesc.indexedBy("id")(); return sorted().some(poss => poss.items.some(stack => { if (stack.itemType.tag !== ItemType.Item.tag) return false; - const item = itemIndex?.get(stack.itemId); + const item = itemIndex.get(stack.itemId); if (!item?.itemListId) return false; - return !!listIndex?.get(item.itemListId); + return !!listIndex.get(item.itemListId); }) ); }); @@ -508,7 +508,7 @@ export const QuestDropDisplay: Component<{ class?: string; }> = (props) => { const questStage = () => props.questDrop.requiredStageId - ? BitCraftTables.QuestStageDesc.indexedBy("id")()?.get(props.questDrop.requiredStageId) + ? BitCraftTables.QuestStageDesc.indexedBy("id")().get(props.questDrop.requiredStageId) : undefined; return ( @@ -569,9 +569,9 @@ export function expandStack( // Plain ItemStack — check if it resolves to an item list const stack = input as ItemStack; if (stack.itemType.tag === ItemType.Item.tag) { - const item = BitCraftTables.ItemDesc.indexedBy("id")?.()?.get(stack.itemId); + const item = BitCraftTables.ItemDesc.indexedBy("id")().get(stack.itemId); if (item?.itemListId) { - const list = BitCraftTables.ItemListDesc.indexedBy("id")?.()?.get(item.itemListId); + const list = BitCraftTables.ItemListDesc.indexedBy("id")().get(item.itemListId); if (list) { return }/>; } diff --git a/frontend/src/components/shared/PlaceableGraph.tsx b/frontend/src/components/shared/PlaceableGraph.tsx index 129fc69..34aabf8 100644 --- a/frontend/src/components/shared/PlaceableGraph.tsx +++ b/frontend/src/components/shared/PlaceableGraph.tsx @@ -786,8 +786,8 @@ export function computeExpectedPath( function aggregateItemCosts( costs: { itemId: number; itemType: string; quantity: number }[], - itemIndex: Map | undefined, - cargoIndex: Map | undefined, + itemIndex: Map, + cargoIndex: Map, ): PathResult["itemCosts"] { const map = new Map(); for (const c of costs) { @@ -798,8 +798,8 @@ function aggregateItemCosts( const [iType, iIdStr] = key.split("-"); const iId = parseInt(iIdStr, 10); const name = iType === "Cargo" - ? cargoIndex?.get(iId)?.name ?? `Cargo #${iId}` - : itemIndex?.get(iId)?.name ?? `Item #${iId}`; + ? cargoIndex.get(iId)?.name ?? `Cargo #${iId}` + : itemIndex.get(iId)?.name ?? `Item #${iId}`; return {itemId: iId, itemType: iType, name, expectedQuantity: qty}; }); } @@ -1043,7 +1043,7 @@ export function PlaceableGraph(props: PlaceableGraphProps) { const renderIcon = () => { if (node.type === "placeable") { - const desc = () => placeableIndex()?.get(node.gameId); + const desc = () => placeableIndex().get(node.gameId); return ( {(d) => } @@ -1052,7 +1052,7 @@ export function PlaceableGraph(props: PlaceableGraphProps) { } else { const isCargo = node.itemType === "Cargo"; if (isCargo) { - const desc = () => cargoIndex()?.get(node.gameId); + const desc = () => cargoIndex().get(node.gameId); return ( {/* hardcoded to use the same size as square icons */} @@ -1060,7 +1060,7 @@ export function PlaceableGraph(props: PlaceableGraphProps) { ); } else { - const desc = () => itemIndex()?.get(node.gameId); + const desc = () => itemIndex().get(node.gameId); return ( {(d) => } @@ -1285,7 +1285,7 @@ export function buildPlaceableGraph(placementId: number): PlaceableGraphData { nodeIds.add(nodeId); const isCargo = itemType === "Cargo"; - const desc = isCargo ? cargoIndex?.get(itemId) : itemIndex?.get(itemId); + const desc = isCargo ? cargoIndex.get(itemId) : itemIndex.get(itemId); const name = desc?.name ?? `${itemType} #${itemId}`; const pp = linkPlacement ? placementByItem.get(`${itemType}-${itemId}`) : undefined; @@ -1308,7 +1308,7 @@ export function buildPlaceableGraph(placementId: number): PlaceableGraphData { if (nodeIds.has(nodeId)) return nodeId; nodeIds.add(nodeId); - const desc = placeableIndex?.get(plcId); + const desc = placeableIndex.get(plcId); nodes.push({ id: nodeId, label: desc?.name ?? `Placeable #${plcId}`, @@ -1388,8 +1388,8 @@ export function buildPlaceableGraph(placementId: number): PlaceableGraphData { const tooltip = ia.consumedItemStacks.length ? ia.consumedItemStacks.map(s => { const name = s.itemType.tag === "Cargo" - ? cargoIndex?.get(s.itemId)?.name ?? `Cargo #${s.itemId}` - : itemIndex?.get(s.itemId)?.name ?? `Item #${s.itemId}`; + ? cargoIndex.get(s.itemId)?.name ?? `Cargo #${s.itemId}` + : itemIndex.get(s.itemId)?.name ?? `Item #${s.itemId}`; return `${s.quantity}× ${name} per action${effortReq}`; }).join(", ") : undefined; diff --git a/frontend/src/components/shared/RecipeDisplay.tsx b/frontend/src/components/shared/RecipeDisplay.tsx index 21d88ae..a088e33 100644 --- a/frontend/src/components/shared/RecipeDisplay.tsx +++ b/frontend/src/components/shared/RecipeDisplay.tsx @@ -129,7 +129,7 @@ const ResourceDepletionIcons: Component<{ resource: ResourceDesc, showLabel: boo } const depletionResource = () => { if (!r?.onDestroyYieldResourceId) return undefined; - return BitCraftTables.ResourceDesc.indexedBy("id")()?.get(r.onDestroyYieldResourceId); + return BitCraftTables.ResourceDesc.indexedBy("id")().get(r.onDestroyYieldResourceId); } return ( @@ -512,7 +512,7 @@ export const renderKnowledgeLockedItem = = (props) => { - const placeable = () => BitCraftTables.PlaceableDesc.indexedBy("id")()?.get(props.placement.placedPlaceableId); + const placeable = () => BitCraftTables.PlaceableDesc.indexedBy("id")().get(props.placement.placedPlaceableId); return ( ; onTogglePercent: () => void; }> = (props) => { - const placeable = () => BitCraftTables.PlaceableDesc.indexedBy("id")()?.get(props.placeableId); + const placeable = () => BitCraftTables.PlaceableDesc.indexedBy("id")().get(props.placeableId); const pct = () => (props.probability / props.totalWeight) * 100; return ( @@ -617,7 +617,7 @@ const GrowthOutcomeIcon: Component<{ export const GrowthPanel: Component<{ growth: PlaceableGrowthDesc }> = (props) => { const [showPercent, setShowPercent] = createSignal(true); - const placeable = () => BitCraftTables.PlaceableDesc.indexedBy("id")()?.get(props.growth.placeableId); + const placeable = () => BitCraftTables.PlaceableDesc.indexedBy("id")().get(props.growth.placeableId); const totalWeight = () => props.growth.outcomes.reduce((sum, o) => sum + o.probability, 0); return ( diff --git a/frontend/src/lib/game-links.tsx b/frontend/src/lib/game-links.tsx index 5ff13fa..cdbeec3 100644 --- a/frontend/src/lib/game-links.tsx +++ b/frontend/src/lib/game-links.tsx @@ -87,7 +87,7 @@ export function SkillLink(props: { skill: SkillDesc; class?: string; showIcon?: /** Resolves a skill ID to a SkillLink, or falls back to plain text. */ export function SkillLinkById(props: { skillId: number; class?: string; showIcon?: boolean; level?: string }) { - const skill = () => BitCraftTables.SkillDesc.indexedBy("id")()?.get(props.skillId); + const skill = () => BitCraftTables.SkillDesc.indexedBy("id")().get(props.skillId); return ( Skill #{props.skillId}}> {s => } @@ -123,7 +123,7 @@ export function KnowledgeLink(props: { id: number; name?: string; class?: string /** Resolves a knowledge ID to a KnowledgeLink. */ export function KnowledgeLinkById(props: { id: number; class?: string; showIcon?: boolean }) { - const knowledge = () => BitCraftTables.SecondaryKnowledgeDesc.indexedBy("id")()?.get(props.id); + const knowledge = () => BitCraftTables.SecondaryKnowledgeDesc.indexedBy("id")().get(props.id); return ; } @@ -144,7 +144,7 @@ export function BuffLink(props: { buffId: number; label?: string; class?: string } export function BuffLinkById(props: { buffId: number; class?: string; showIcon?: boolean, duration?: number }) { - const buff = () => BitCraftTables.BuffDesc.indexedBy("id")()?.get(props.buffId); + const buff = () => BitCraftTables.BuffDesc.indexedBy("id")().get(props.buffId); return ; } @@ -166,7 +166,7 @@ export function BuildingLink(props: { id: number; name?: string; class?: string; /** Resolves a building ID to a BuildingLink. */ export function BuildingLinkById(props: { id: number; class?: string; showIcon?: boolean }) { - const building = () => BitCraftTables.BuildingDesc.indexedBy("id")()?.get(props.id); + const building = () => BitCraftTables.BuildingDesc.indexedBy("id")().get(props.id); return ; } @@ -220,7 +220,7 @@ export function QuestChainLink(props: { id: number; name?: string; class?: strin /** Resolves a quest chain ID to a QuestChainLink. */ export function QuestChainLinkById(props: { id: number; class?: string; showIcon?: boolean }) { - const quest = () => BitCraftTables.QuestChainDesc.indexedBy("id")()?.get(props.id); + const quest = () => BitCraftTables.QuestChainDesc.indexedBy("id")().get(props.id); return ; } @@ -242,7 +242,7 @@ export function CollectibleLink(props: { id: number; name?: string; class?: stri /** Resolves a collectible ID to a CollectibleLink. */ export function CollectibleLinkById(props: { id: number; class?: string; showIcon?: boolean }) { - const col = () => BitCraftTables.CollectibleDesc.indexedBy("id")()?.get(props.id); + const col = () => BitCraftTables.CollectibleDesc.indexedBy("id")().get(props.id); return ; } @@ -264,7 +264,7 @@ export function PlaceableLink(props: { id: number; name?: string; class?: string /** Resolves a placeable ID to a PlaceableLink. */ export function PlaceableLinkById(props: { id: number; class?: string; showIcon?: boolean }) { - const plc = () => BitCraftTables.PlaceableDesc.indexedBy("id")()?.get(props.id); + const plc = () => BitCraftTables.PlaceableDesc.indexedBy("id")().get(props.id); return ; } @@ -342,9 +342,9 @@ export function ItemStackLink(props: { stack: ItemStack; class?: string; showIco const name = () => { if (isCargo()) { - return BitCraftTables.CargoDesc.indexedBy("id")()?.get(props.stack.itemId)?.name ?? `Cargo #${props.stack.itemId}`; + return BitCraftTables.CargoDesc.indexedBy("id")().get(props.stack.itemId)?.name ?? `Cargo #${props.stack.itemId}`; } - return BitCraftTables.ItemDesc.indexedBy("id")()?.get(props.stack.itemId)?.name ?? `Item #${props.stack.itemId}`; + return BitCraftTables.ItemDesc.indexedBy("id")().get(props.stack.itemId)?.name ?? `Item #${props.stack.itemId}`; }; const href = () => isCargo() diff --git a/frontend/src/lib/placeables.ts b/frontend/src/lib/placeables.ts index 4cec3fe..6908cd6 100644 --- a/frontend/src/lib/placeables.ts +++ b/frontend/src/lib/placeables.ts @@ -185,7 +185,7 @@ export function findRootPlacement(placeableId: number): PlaceablePlacementDesc | // ─── Naming Helpers ───────────────────────────────────────────── export function getPlaceableName(placeableId: number): string { - return BitCraftTables.PlaceableDesc.indexedBy("id")()?.get(placeableId)?.name ?? `Placeable #${placeableId}`; + return BitCraftTables.PlaceableDesc.indexedBy("id")().get(placeableId)?.name ?? `Placeable #${placeableId}`; } export function getPlacementName(p: PlaceablePlacementDesc): string { diff --git a/frontend/src/lib/recipe-sources.tsx b/frontend/src/lib/recipe-sources.tsx index de5801c..b6a69fd 100644 --- a/frontend/src/lib/recipe-sources.tsx +++ b/frontend/src/lib/recipe-sources.tsx @@ -116,8 +116,8 @@ function addCommonRequirements( }, totalEffort?: number ) { - const skillData = BitCraftTables.SkillDesc.indexedBy("id")()!; - const toolData = BitCraftTables.ToolTypeDesc.indexedBy("id")()!; + const skillData = BitCraftTables.SkillDesc.indexedBy("id")(); + const toolData = BitCraftTables.ToolTypeDesc.indexedBy("id")(); recipe.levelRequirements.forEach(req => { const pair = skillReqPair(req, skillData); @@ -147,7 +147,7 @@ function addUseHandsInformation(lines: StatLine[], recipe: { toolRequirements: T // ─── Per-Type Stat Line Extractors ────────────────────────────── export function craftingStatLines(recipe: CraftingRecipeDesc): StatLine[] { - const buildingData = BitCraftTables.BuildingTypeDesc.indexedBy("id")()!; + const buildingData = BitCraftTables.BuildingTypeDesc.indexedBy("id")(); const lines: StatLine[] = [ ["Effort:", recipe.actionsRequired], ["Time:", fixFloat(recipe.timeRequirement)], @@ -251,7 +251,7 @@ export function conversionStatLines(recipe: ItemConversionRecipeDesc): StatLine[ } export function travelerTaskStatLines(task: TravelerTaskDesc): StatLine[] { - const skillData = BitCraftTables.SkillDesc.indexedBy("id")()!; + const skillData = BitCraftTables.SkillDesc.indexedBy("id")(); const skill = skillData.get(task.levelRequirement.skillId); let skillTag = skill?.skillCategory.tag ?? ""; if (skillTag === "Adventure") skillTag = "Skill"; @@ -271,7 +271,7 @@ export function travelerTaskStatLines(task: TravelerTaskDesc): StatLine[] { } export function travelerTradeStatLines(trade: TravelerTradeOrderDesc): StatLine[] { - const skillData = BitCraftTables.SkillDesc.indexedBy("id")()!; + const skillData = BitCraftTables.SkillDesc.indexedBy("id")(); const npcName = getTravelerNpcName(trade.traveler.tag); const lines: StatLine[] = [["Traveler:", npcName]]; trade.levelRequirements.forEach(req => { @@ -316,7 +316,7 @@ export function placementStatLines(placement: PlaceablePlacementDesc): StatLine[ // Biome requirements if (placement.requiredBiomes.length) { const biomeOrdinals = BitCraftTables.PlaceablePlacementDesc.tagToOrdinal("requiredBiomes"); - const biomeIndex = BitCraftTables.BiomeDesc.indexedBy("biomeType"); + const biomeIndex = BitCraftTables.BiomeDesc.indexedBy("biomeType", true); function tagToBiomeLink(tag: string) { const descId = biomeOrdinals.get(tag); const desc = biomeIndex().get(descId); @@ -340,8 +340,8 @@ export function placementStatLines(placement: PlaceablePlacementDesc): StatLine[ } // Level/tool/knowledge requirements - const skillData = BitCraftTables.SkillDesc.indexedBy("id")()!; - const toolData = BitCraftTables.ToolTypeDesc.indexedBy("id")()!; + const skillData = BitCraftTables.SkillDesc.indexedBy("id")(); + const toolData = BitCraftTables.ToolTypeDesc.indexedBy("id")(); placement.levelRequirements.forEach(req => { const pair = skillReqPair(req, skillData); if (pair) lines.push(pair); @@ -360,7 +360,7 @@ export function placementStatLines(placement: PlaceablePlacementDesc): StatLine[ if (placement.buildings.length) { const buildingIndex = BitCraftTables.BuildingDesc.indexedBy("id")(); const names = placement.buildings - .map(id => buildingIndex?.get(id)?.name ?? `Building #${id}`) + .map(id => buildingIndex.get(id)?.name ?? `Building #${id}`) .join(", "); lines.push([Near Building:, `${names} (≤${placement.maxDistanceToBuildings}m)`]); } diff --git a/frontend/src/lib/relations.ts b/frontend/src/lib/relations.ts index dba6978..0610d70 100644 --- a/frontend/src/lib/relations.ts +++ b/frontend/src/lib/relations.ts @@ -66,9 +66,9 @@ function anyProbStackMatches(stacks: ProbabilisticItemStack[], itemId: number, i if (matchesStack(s.itemStack, itemId, itemType)) return true; // Also check if the inner item resolves to an item list containing the target if (s.itemStack.itemType.tag === ItemType.Item.tag) { - const item = BitCraftTables.ItemDesc.indexedBy("id")?.()?.get(s.itemStack.itemId); + const item = BitCraftTables.ItemDesc.indexedBy("id")().get(s.itemStack.itemId); if (item?.itemListId) { - const list = BitCraftTables.ItemListDesc.indexedBy("id")?.()?.get(item.itemListId); + const list = BitCraftTables.ItemListDesc.indexedBy("id")().get(item.itemListId); if (list) return anyPossibilityMatches(list.possibilities, itemId, itemType); } } @@ -106,16 +106,16 @@ export function craftingRecipesConsuming(itemId: number, itemType: string): Craf // ─── Extraction Recipes ───────────────────────────────────────── export function resourceGrowthFrom(resourceId: number): ResourceGrowthRecipeDesc[] | undefined { - return BitCraftTables.ResourceGrowthRecipeDesc.indexedByMulti("grownResourceId")?.()?.get(resourceId); + return BitCraftTables.ResourceGrowthRecipeDesc.indexedByMulti("grownResourceId")().get(resourceId); } export function resourceGrowthInto(resourceId: number): ResourceGrowthRecipeDesc | undefined { - return BitCraftTables.ResourceGrowthRecipeDesc.indexedBy("resourceId")?.()?.get(resourceId); + return BitCraftTables.ResourceGrowthRecipeDesc.indexedBy("resourceId")().get(resourceId); } /** The extraction recipe for a given resource */ export function extractionRecipeForResource(resourceId: number): ExtractionRecipeDesc | undefined { - return BitCraftTables.ExtractionRecipeDesc.indexedBy("resourceId")()?.get(resourceId); + return BitCraftTables.ExtractionRecipeDesc.indexedBy("resourceId")().get(resourceId); } /** Extraction recipes that drop this item/cargo */ @@ -151,7 +151,7 @@ export function terraformRecipesDropping(itemId: number, itemType: string): Terr /** The construction recipe for a given building */ export function constructionRecipeForBuilding(buildingId: number): ConstructionRecipeDesc | undefined { - return BitCraftTables.ConstructionRecipeDesc.indexedBy("buildingDescriptionId")()?.get(buildingId); + return BitCraftTables.ConstructionRecipeDesc.indexedBy("buildingDescriptionId")().get(buildingId); } /** Construction recipes that consume this item/cargo */ @@ -168,7 +168,7 @@ export function constructionRecipesConsuming(itemId: number, itemType: string): /** The deconstruction recipe for a given building */ export function deconstructionRecipeForBuilding(buildingId: number): DeconstructionRecipeDesc | undefined { - return BitCraftTables.DeconstructionRecipeDesc.indexedBy("consumedBuilding")()?.get(buildingId); + return BitCraftTables.DeconstructionRecipeDesc.indexedBy("consumedBuilding")().get(buildingId); } /** Deconstruction recipes that produce this item/cargo as output */ @@ -277,7 +277,7 @@ export function getItemListSource(il: ItemListDesc | undefined): ItemListSource // NB should also be multi but doesn't happen in practice const lootDescs = BitCraftTables.ContributionLootDesc.indexedBy("itemListId"); const matchedLoot = lootDescs().get(il.id); - if (matchedLoot) return {type: "Enemy", loot: matchedLoot, enemy: enemyIndex()?.get(matchedLoot.enemyTypeId)}; + if (matchedLoot) return {type: "Enemy", loot: matchedLoot, enemy: enemyIndex().get(matchedLoot.enemyTypeId)}; return {type: "Unknown"}; } @@ -328,7 +328,6 @@ export function enemiesForResource(resource: ResourceDesc): EnemyDesc[] { if (!resource.enemyParamsId?.length) return []; const paramIdx = BitCraftTables.EnemyAiParamsDesc.indexedBy("id")(); const enemyIdx = BitCraftTables.EnemyDesc.indexedBy("enemyType")(); - if (!paramIdx || !enemyIdx) return []; return resource.enemyParamsId .map(id => { const p = paramIdx.get(id); @@ -348,7 +347,6 @@ export function prospectingForBiome(biomeType: number): ProspectingDesc[] { export function achievementPrereqs(requisites: number[]): AchievementDesc[] { if (!requisites?.length) return []; const idx = BitCraftTables.AchievementDesc.indexedBy("id")(); - if (!idx) return []; return requisites.map(id => idx.get(id)).filter((v): v is AchievementDesc => !!v); } @@ -356,7 +354,6 @@ export function achievementPrereqs(requisites: number[]): AchievementDesc[] { export function collectibleRewards(collectibleIds: number[]): CollectibleDesc[] { if (!collectibleIds?.length) return []; const idx = BitCraftTables.CollectibleDesc.indexedBy("id")(); - if (!idx) return []; return collectibleIds.map(id => idx.get(id)).filter((v): v is CollectibleDesc => !!v); } @@ -364,8 +361,8 @@ export function collectibleRewards(collectibleIds: number[]): CollectibleDesc[] /** Resolve a crafting recipe's display name, substituting item names into the template */ export function getCraftingRecipeName(recipe: CraftingRecipeDesc): string { - const itemData = BitCraftTables.ItemDesc.indexedBy("id")()!; - const cargoData = BitCraftTables.CargoDesc.indexedBy("id")()!; + const itemData = BitCraftTables.ItemDesc.indexedBy("id")(); + const cargoData = BitCraftTables.CargoDesc.indexedBy("id")(); const mainOutput = recipe.craftedItemStacks.at(0); const mainInput = recipe.consumedItemStacks.at(0); if (!mainOutput) return recipe.name; @@ -388,10 +385,10 @@ export function getCraftingRecipeName(recipe: CraftingRecipeDesc): string { /** Display name for an extraction recipe */ export function getExtractionRecipeName(recipe: ExtractionRecipeDesc): string { const resource = recipe.resourceId - ? BitCraftTables.ResourceDesc.indexedBy("id")()?.get(recipe.resourceId) + ? BitCraftTables.ResourceDesc.indexedBy("id")().get(recipe.resourceId) : undefined; const cargo = recipe.cargoId - ? BitCraftTables.CargoDesc.indexedBy("id")()?.get(recipe.cargoId) + ? BitCraftTables.CargoDesc.indexedBy("id")().get(recipe.cargoId) : undefined; return recipe.verbPhrase + " " + (resource?.name ?? cargo?.name ?? "Unknown"); } @@ -399,12 +396,12 @@ export function getExtractionRecipeName(recipe: ExtractionRecipeDesc): string { /** Display name for a construction recipe */ export function getConstructionRecipeName(recipe: ConstructionRecipeDesc): string { // wild RHS but it doesn't ever occur - return recipe.name || ("Construct " + (BitCraftTables.BuildingDesc.indexedBy("id")()?.get(recipe.buildingDescriptionId)?.name ?? ("Building #" + recipe.buildingDescriptionId))); + return recipe.name || ("Construct " + (BitCraftTables.BuildingDesc.indexedBy("id")().get(recipe.buildingDescriptionId)?.name ?? ("Building #" + recipe.buildingDescriptionId))); } /** Display name for a deconstruction recipe */ export function getDeconstructionRecipeName(recipe: DeconstructionRecipeDesc): string { - const building = BitCraftTables.BuildingDesc.indexedBy("id")()?.get(recipe.consumedBuilding); + const building = BitCraftTables.BuildingDesc.indexedBy("id")().get(recipe.consumedBuilding); return "Deconstruct " + (building?.name ?? "Unknown"); } @@ -415,7 +412,7 @@ export function getConversionRecipeName(recipe: ItemConversionRecipeDesc): strin /** Display name for a traveler task */ export function getTravelerTaskName(task: TravelerTaskDesc): string { - const skill = BitCraftTables.SkillDesc.indexedBy("id")()?.get(task.levelRequirement.skillId); + const skill = BitCraftTables.SkillDesc.indexedBy("id")().get(task.levelRequirement.skillId); const firstItem = task.requiredItems.find(s => !isHexCoin(s)) ?? task.rewardedItems.find(s => !isHexCoin(s)); const pfx = (skill?.name ? skill.name + " " : "") + " Task: "; return pfx + getItemStackName(firstItem); @@ -426,7 +423,7 @@ export function getTravelerNpcName(travelerTag: string): string { const tagOrdinal = BitCraftTables.TravelerTradeOrderDesc.tagToOrdinal("traveler"); const npcOrdinal = tagOrdinal.get(travelerTag); if (npcOrdinal !== undefined) { - return BitCraftTables.NpcDesc.indexedBy("npcType")()?.get(npcOrdinal)?.name ?? travelerTag; + return BitCraftTables.NpcDesc.indexedBy("npcType")().get(npcOrdinal)?.name ?? travelerTag; } return travelerTag; } @@ -483,17 +480,17 @@ export function getResourceDepletionName(resource: ResourceDesc): string { /** Get the resource associated with an extraction recipe (if any) */ export function resourceForExtraction(recipe: ExtractionRecipeDesc): ResourceDesc | undefined { if (!recipe.resourceId) return undefined; - return BitCraftTables.ResourceDesc.indexedBy("id")()?.get(recipe.resourceId); + return BitCraftTables.ResourceDesc.indexedBy("id")().get(recipe.resourceId); } /** Get the building associated with a deconstruction recipe */ export function buildingForDeconstruction(recipe: DeconstructionRecipeDesc): BuildingDesc | undefined { - return BitCraftTables.BuildingDesc.indexedBy("id")()?.get(recipe.consumedBuilding); + return BitCraftTables.BuildingDesc.indexedBy("id")().get(recipe.consumedBuilding); } /** Get the building associated with a construction recipe */ export function buildingForConstruction(recipe: ConstructionRecipeDesc): BuildingDesc | undefined { - return BitCraftTables.BuildingDesc.indexedBy("id")()?.get(recipe.buildingDescriptionId); + return BitCraftTables.BuildingDesc.indexedBy("id")().get(recipe.buildingDescriptionId); } // ─── Enemy Drops ───────────────────────────────────────────────── @@ -596,7 +593,7 @@ export function questsWithStageCondition(tag: string, id: number): QuestChainDes } if (!chainIds.size) return []; const chainIndex = BitCraftTables.QuestChainDesc.indexedBy("id")(); - return Array.from(chainIds).map(id => chainIndex?.get(id)).filter((q): q is QuestChainDesc => !!q); + return Array.from(chainIds).map(id => chainIndex.get(id)).filter((q): q is QuestChainDesc => !!q); } /** Quest chains whose stages require this item/cargo via ItemStack or EquippedItem conditions */ @@ -616,7 +613,7 @@ export function questsWithStageConditionItem(itemId: number, itemType: string): } if (!chainIds.size) return []; const chainIndex = BitCraftTables.QuestChainDesc.indexedBy("id")(); - return Array.from(chainIds).map(id => chainIndex?.get(id)).filter((q): q is QuestChainDesc => !!q); + return Array.from(chainIds).map(id => chainIndex.get(id)).filter((q): q is QuestChainDesc => !!q); } /** @@ -644,17 +641,17 @@ export function questDropSourcesFor(itemId: number, itemType: string): { for (const qd of drops) { if (qd.extractionId > 0 && !exIds.has(qd.extractionId)) { exIds.add(qd.extractionId); - const r = exIndex?.get(qd.extractionId); + const r = exIndex.get(qd.extractionId); if (r) extractionRecipes.push(r); } if (qd.enemyId > 0 && !enIds.has(qd.enemyId)) { enIds.add(qd.enemyId); - const e = enIndex?.get(qd.enemyId); + const e = enIndex.get(qd.enemyId); if (e) enemies.push(e); } if (qd.itemListId > 0 && !ilIds.has(qd.itemListId)) { ilIds.add(qd.itemListId); - const l = ilIndex?.get(qd.itemListId); + const l = ilIndex.get(qd.itemListId); if (l) itemLists.push(l); } } diff --git a/frontend/src/lib/spacetime.ts b/frontend/src/lib/spacetime.ts index 512f535..5dfbba7 100644 --- a/frontend/src/lib/spacetime.ts +++ b/frontend/src/lib/spacetime.ts @@ -78,7 +78,7 @@ async function fetchBSATN(params: FetchParams) { function cache(n: string, b: { getTypeScriptAlgebraicType: () => AlgebraicType }) { const base = new BitCraftTable(n, b.getTypeScriptAlgebraicType()); const [resource] = createResource(async () => - fetchBSATN({name: base.st_name, itemType: base.st_type}) + fetchBSATN({name: base.spacetimeName, itemType: base.spacetimeType}) ); base.get = resource; base.loading = () => resource.loading; @@ -102,14 +102,14 @@ function createIndex( - tbl: Accessor, field: TIdx + tbl: Accessor, field: TIdx, allowZero: boolean ): Accessor> { return createMemo(() => { const data = tbl() ?? []; const map = new Map(); for (const item of data) { const key = item[field] as TValue; - if (key !== null && key !== 0) { // idk a big list of 0 values seems useless but if it's needed later I guess it's an easy change + if (key !== null && (allowZero || key !== 0)) { let existing = map.get(key); if (existing) { existing.push(item); @@ -128,8 +128,8 @@ export function loadTableAdHoc(n: string, b: { getTypeScriptAlgebraicType: () } export class BitCraftTable { - st_name: string; - st_type: AlgebraicType; + spacetimeName: string; + spacetimeType: AlgebraicType; get: Accessor; loading: Accessor; error: Accessor; @@ -137,9 +137,9 @@ export class BitCraftTable { #idxCacheMulti: Map>>; #tagOrdinalCache: Map>; - constructor(st_name: string, st_type: AlgebraicType) { - this.st_name = st_name; - this.st_type = st_type; + constructor(spacetimeName: string, spacetimeType: AlgebraicType) { + this.spacetimeName = spacetimeName; + this.spacetimeType = spacetimeType; this.get = () => undefined; this.loading = () => true; this.error = () => undefined; @@ -157,10 +157,10 @@ export class BitCraftTable { return res; } - indexedByMulti(key: TIdx): Accessor> { + indexedByMulti(key: TIdx, allowZero: boolean = false): Accessor> { let res = this.#idxCacheMulti.get(key); if (!res) { - res = createIndexMulti(this.get, key); + res = createIndexMulti(this.get, key, allowZero); this.#idxCacheMulti.set(key, res) } return res; @@ -176,10 +176,10 @@ export class BitCraftTable { const cached = this.#tagOrdinalCache.get(field); if (cached) return cached; - const elements: any[] = (this.st_type as any).product?.elements ?? []; + const elements: any[] = (this.spacetimeType as any).product?.elements ?? []; const elem = elements.find((e: any) => e.name === field); if (!elem) { - throw new Error(`[BitCraftTable] Field '${field}' not found in type '${this.st_name}'`); + throw new Error(`[BitCraftTable] Field '${field}' not found in type '${this.spacetimeName}'`); } const rootType: AlgebraicType = elem.algebraicType; const variants: any[] = rootType.type == "ArrayType" ? rootType.array.sum.variants : rootType.sum.variants; diff --git a/frontend/src/lib/table-defs/achievement-table.tsx b/frontend/src/lib/table-defs/achievement-table.tsx index 90d7cf8..9871e7c 100644 --- a/frontend/src/lib/table-defs/achievement-table.tsx +++ b/frontend/src/lib/table-defs/achievement-table.tsx @@ -14,7 +14,7 @@ export const AchievementDefs: BitCraftToDataDef = { id: "Prerequisites", accessorFn: (row) => { const idx = BitCraftTables.AchievementDesc.indexedBy("id"); - return row.requisites?.map(id => idx?.()?.get(id)?.name ?? `#${id}`).join(", ") || ""; + return row.requisites?.map(id => idx().get(id)?.name ?? `#${id}`).join(", ") || ""; }, cell: (props) => { const row = props.row.original; @@ -23,7 +23,7 @@ export const AchievementDefs: BitCraftToDataDef = { return ( {row.requisites.map(id => { - const ach = idx?.()?.get(id); + const ach = idx().get(id); return ; })} diff --git a/frontend/src/lib/table-defs/buffs-table.tsx b/frontend/src/lib/table-defs/buffs-table.tsx index 978e6e0..5b74502 100644 --- a/frontend/src/lib/table-defs/buffs-table.tsx +++ b/frontend/src/lib/table-defs/buffs-table.tsx @@ -19,7 +19,7 @@ export const BuffDefs: BitCraftToDataDef = { descriptionColumn(), { id: "Buff Type", - accessorFn: row => row.buffTypeId ? BitCraftTables.BuffTypeDesc.indexedBy("id")?.()?.get(row.buffTypeId)?.name ?? `#${row.buffTypeId}` : undefined, + accessorFn: row => row.buffTypeId ? BitCraftTables.BuffTypeDesc.indexedBy("id")().get(row.buffTypeId)?.name ?? `#${row.buffTypeId}` : undefined, filterFn: includedIn(), }, statsColumn(), diff --git a/frontend/src/lib/table-defs/buildings-table.tsx b/frontend/src/lib/table-defs/buildings-table.tsx index 4ab37a3..99ff68d 100644 --- a/frontend/src/lib/table-defs/buildings-table.tsx +++ b/frontend/src/lib/table-defs/buildings-table.tsx @@ -36,11 +36,11 @@ export const BuildingDescDefs: BitCraftToDataDef = { id: "Type", accessorFn: (bldg: BuildingDesc) => { const bldgTypes = BitCraftTables.BuildingTypeDesc.indexedBy("id"); - return bldg.functions.map(func => bldgTypes()!.get(func.functionType)?.name || "Unknown"); + return bldg.functions.map(func => bldgTypes().get(func.functionType)?.name || "Unknown"); }, getUniqueValues: (bldg: BuildingDesc) => { const bldgTypes = BitCraftTables.BuildingTypeDesc.indexedBy("id"); - return bldg.functions.map(func => bldgTypes()!.get(func.functionType)?.name || "Unknown"); + return bldg.functions.map(func => bldgTypes().get(func.functionType)?.name || "Unknown"); }, filterFn: "arrIncludesSome" }, diff --git a/frontend/src/lib/table-defs/cargo-table.tsx b/frontend/src/lib/table-defs/cargo-table.tsx index 6600eae..d33af56 100644 --- a/frontend/src/lib/table-defs/cargo-table.tsx +++ b/frontend/src/lib/table-defs/cargo-table.tsx @@ -27,7 +27,7 @@ export const CargoDescDefs: BitCraftToDataDef = { rarityColumn(), { id: "Supply", - accessorFn: (cargo: CargoDesc) => BitCraftTables.BuildingRepairsDesc.indexedBy("cargoId")?.()?.get(cargo.id)?.repairValue, + accessorFn: (cargo: CargoDesc) => BitCraftTables.BuildingRepairsDesc.indexedBy("cargoId")().get(cargo.id)?.repairValue, filterFn: 'inNumberRange', sortUndefined: 'last' }, diff --git a/frontend/src/lib/table-defs/collectible-table.tsx b/frontend/src/lib/table-defs/collectible-table.tsx index e822cc0..24a2f2e 100644 --- a/frontend/src/lib/table-defs/collectible-table.tsx +++ b/frontend/src/lib/table-defs/collectible-table.tsx @@ -27,10 +27,9 @@ export const CollectibleDefs: BitCraftToDataDef = { id: "Item Deed", accessorKey: "itemDeedId", cell: (props) => { - const itemIdx = BitCraftTables.ItemDesc.indexedBy("id"); const item = props.getValue(); if (!item) return <>; - const itemDesc = itemIdx?.()?.get(item); + const itemDesc = BitCraftTables.ItemDesc.indexedBy("id")().get(item); return itemDesc ? : <>; }, }, diff --git a/frontend/src/lib/table-defs/combat-table.tsx b/frontend/src/lib/table-defs/combat-table.tsx index 7607862..df77e24 100644 --- a/frontend/src/lib/table-defs/combat-table.tsx +++ b/frontend/src/lib/table-defs/combat-table.tsx @@ -17,8 +17,8 @@ export const CombatDefs: BitCraftToDataDef = { { id: "Weapon Type", accessorFn: (row) => { - const idx = BitCraftTables.WeaponTypeDesc.indexedBy("id"); - return row.weaponTypeRequirements?.map(id => idx?.()?.get(id)?.name ?? `#${id}`).join(", "); + const idx = BitCraftTables.WeaponTypeDesc.indexedBy("id")(); + return row.weaponTypeRequirements?.map(id => idx.get(id)?.name ?? `#${id}`).join(", "); }, filterFn: includedIn() }, diff --git a/frontend/src/lib/table-defs/deployables-table.tsx b/frontend/src/lib/table-defs/deployables-table.tsx index 6baec2e..0583cbf 100644 --- a/frontend/src/lib/table-defs/deployables-table.tsx +++ b/frontend/src/lib/table-defs/deployables-table.tsx @@ -19,19 +19,19 @@ function requiredItemsForDeployable(dep: DeployableDesc): { deed: ItemDesc | undefined; training: SecondaryKnowledgeDesc[]; } { - const collTable = BitCraftTables.CollectibleDesc.indexedBy("id")!()!; + const collTable = BitCraftTables.CollectibleDesc.indexedBy("id")(); const collId = dep.deployFromCollectibleId; if (!collId) return {deed: undefined, training: []}; const collectible = collTable.get(collId); if (!collectible) return {deed: undefined, training: []}; - const itemIndex = BitCraftTables.ItemDesc.indexedBy("id")!()!; + const itemIndex = BitCraftTables.ItemDesc.indexedBy("id")(); const deedItem = collectible.itemDeedId ? itemIndex.get(collectible.itemDeedId) : undefined; const knowledgeIds = collectible.requiredKnowledgesToUse ?? []; let knowledgeDescs: SecondaryKnowledgeDesc[] = []; if (knowledgeIds.length) { - const knowledgeIndex = BitCraftTables.SecondaryKnowledgeDesc.indexedBy("id")!()!; + const knowledgeIndex = BitCraftTables.SecondaryKnowledgeDesc.indexedBy("id")(); knowledgeDescs = knowledgeIds .map(k => knowledgeIndex.get(k)) .filter((k): k is SecondaryKnowledgeDesc => !!k); @@ -40,7 +40,7 @@ function requiredItemsForDeployable(dep: DeployableDesc): { } function getStepHeight(deployable: DeployableDesc): number | null { - const pathfinding = BitCraftTables.PathfindingDesc.indexedBy("id")!()!; + const pathfinding = BitCraftTables.PathfindingDesc.indexedBy("id")(); if (deployable.movementType.tag === MovementType.Water.tag) { return pathfinding.get(deployable.pathfindingId)?.maxSwimHeightDelta || null; } @@ -65,7 +65,7 @@ export const DeployableDescDefs: BitCraftToDataDef = { route: dep => ["deployable", dep.id], prefixElement: dep => { const collTable = BitCraftTables.CollectibleDesc.indexedBy("id"); - const collectible = collTable?.()?.get(dep.deployFromCollectibleId); + const collectible = collTable().get(dep.deployFromCollectibleId); // this should be noInteract because it links to the collectible rather than the deployable itself return collectible?.iconAssetName ? : <>; }, @@ -185,7 +185,7 @@ export const DeployableDescDefs: BitCraftToDataDef = { statsColumn(), rowActions({accessorKey: "id"}, "col", undefined, { accessorFn: (dep) => { - return BitCraftTables.CollectibleDesc.indexedBy("id")?.()?.get(dep.deployFromCollectibleId)?.id; + return BitCraftTables.CollectibleDesc.indexedBy("id")().get(dep.deployFromCollectibleId)?.id; } }), ], diff --git a/frontend/src/lib/table-defs/equipment-table.tsx b/frontend/src/lib/table-defs/equipment-table.tsx index 7624f0f..272a46d 100644 --- a/frontend/src/lib/table-defs/equipment-table.tsx +++ b/frontend/src/lib/table-defs/equipment-table.tsx @@ -12,10 +12,10 @@ export const EquipmentDefs: BitCraftToDataDef = { columns: [ headerColumn({ title: "Name", - accessor: {accessorFn: eq => BitCraftTables.ItemDesc.indexedBy("id")?.()?.get(eq.itemId)?.name ?? `Item #${eq.itemId}`}, + accessor: {accessorFn: eq => BitCraftTables.ItemDesc.indexedBy("id")().get(eq.itemId)?.name ?? `Item #${eq.itemId}`}, route: eq => ["equipment", eq.itemId], prefixElement: eq => { - const item = BitCraftTables.ItemDesc.indexedBy("id")?.()?.get(eq.itemId); + const item = BitCraftTables.ItemDesc.indexedBy("id")().get(eq.itemId); return item ? : <>; }, }), @@ -31,7 +31,7 @@ export const EquipmentDefs: BitCraftToDataDef = { accessorFn: row => { if (!row.levelRequirement) return undefined; if (!row.levelRequirement.skillId) return undefined; - const skill = BitCraftTables.SkillDesc.indexedBy("id")?.()?.get(row.levelRequirement.skillId); + const skill = BitCraftTables.SkillDesc.indexedBy("id")().get(row.levelRequirement.skillId); return skill?.name ?? `#${row.levelRequirement.skillId}`; }, cell: (props) => { @@ -47,8 +47,8 @@ export const EquipmentDefs: BitCraftToDataDef = { filterFn: "inNumberRange", }, statsColumn(), - tierColumn({accessorFn: equip => BitCraftTables.ItemDesc.indexedBy("id")?.().get(equip.itemId)?.tier ?? -1}), - rarityColumn({accessorFn: equip => BitCraftTables.ItemDesc.indexedBy("id")?.().get(equip.itemId)?.rarity.tag ?? Rarity.Default.tag as Rarity["tag"]}), // idk why TS needs this + tierColumn({accessorFn: equip => BitCraftTables.ItemDesc.indexedBy("id")().get(equip.itemId)?.tier ?? -1}), + rarityColumn({accessorFn: equip => BitCraftTables.ItemDesc.indexedBy("id")().get(equip.itemId)?.rarity.tag ?? Rarity.Default.tag as Rarity["tag"]}), // idk why TS needs this rowActions({accessorKey: "itemId"}, "item"), ], facetedFilters: [ diff --git a/frontend/src/lib/table-defs/food-table.tsx b/frontend/src/lib/table-defs/food-table.tsx index 387d9f1..83a3793 100644 --- a/frontend/src/lib/table-defs/food-table.tsx +++ b/frontend/src/lib/table-defs/food-table.tsx @@ -28,7 +28,7 @@ const foodToBuffNames = (food: FoodDesc, def: any) => { if (!food.buffs.length) return def; const buffIdx = BitCraftTables.BuffDesc.indexedBy("id"); return food.buffs.map(b => { - const buff = buffIdx()?.get(b.buffId); + const buff = buffIdx().get(b.buffId); return buff?.description ?? `Buff #${b.buffId}`; }); } @@ -37,10 +37,10 @@ export const FoodDefs: BitCraftToDataDef = { columns: [ headerColumn({ title: "Name", - accessor: {accessorFn: food => BitCraftTables.ItemDesc.indexedBy("id")()?.get(food.itemId)?.name ?? `Item #${food.itemId}`}, + accessor: {accessorFn: food => BitCraftTables.ItemDesc.indexedBy("id")().get(food.itemId)?.name ?? `Item #${food.itemId}`}, route: food => ["food", food.itemId], prefixElement: food => { - const item = BitCraftTables.ItemDesc.indexedBy("id")()?.get(food.itemId); + const item = BitCraftTables.ItemDesc.indexedBy("id")().get(food.itemId); return item ? : <>; }, }), @@ -52,8 +52,8 @@ export const FoodDefs: BitCraftToDataDef = { cell: p => { const food = p.row.original; if (!food.buffs?.length) return undefined; - const buffIdx = BitCraftTables.BuffDesc.indexedBy("id"); - const buffs = food.buffs.map(b => [b, buffIdx()?.get(b.buffId)]).filter((b) => !!b[1]) as [BuffEffect, BuffDesc][]; + const buffIdx = BitCraftTables.BuffDesc.indexedBy("id")(); + const buffs = food.buffs.map(b => [b, buffIdx.get(b.buffId)]).filter((b) => !!b[1]) as [BuffEffect, BuffDesc][]; return (
    @@ -66,8 +66,8 @@ export const FoodDefs: BitCraftToDataDef = { ); }, sortingFn: (a, b) => { - const buffIdx = BitCraftTables.BuffDesc.indexedBy("id"); - const getMax = (food: FoodDesc) => Math.max(...food.buffs?.map(be => be.duration ?? buffIdx().get(be.buffId)?.duration ?? 0)); + const buffIdx = BitCraftTables.BuffDesc.indexedBy("id")(); + const getMax = (food: FoodDesc) => Math.max(...food.buffs?.map(be => be.duration ?? buffIdx.get(be.buffId)?.duration ?? 0)); return getMax(a.original) - getMax(b.original); }, sortUndefined: "last", @@ -77,9 +77,9 @@ export const FoodDefs: BitCraftToDataDef = { accessorFn: (food) => { const buffs = food.buffs; if (!buffs?.length) return undefined; - const buffIdx = BitCraftTables.BuffDesc.indexedBy("id"); + const buffIdx = BitCraftTables.BuffDesc.indexedBy("id")(); return consolidateStats(buffs.flatMap(buffEffect => { - const buffDesc = buffIdx()?.get(buffEffect.buffId); + const buffDesc = buffIdx.get(buffEffect.buffId); if (!buffDesc) return null; return buffDesc.stats; }).filter((v): v is CsvStatEntry => v !== null)); @@ -91,8 +91,8 @@ export const FoodDefs: BitCraftToDataDef = { {id: "Stamina", accessorKey: "stamina", cell: p => {fixFloat(p.getValue() as number)}, filterFn: "inNumberRange"}, {id: "Up To Stamina", accessorKey: "upToStamina", cell: p => {fixFloat(p.getValue() as number)}, filterFn: "inNumberRange"}, boolColumn("Consumable In Combat", {accessorKey: "consumableWhileInCombat"}), - tierColumn({accessorFn: tool => BitCraftTables.ItemDesc.indexedBy("id")?.().get(tool.itemId)?.tier ?? -1}), - rarityColumn({accessorFn: tool => BitCraftTables.ItemDesc.indexedBy("id")?.().get(tool.itemId)?.rarity.tag ?? Rarity.Default.tag as Rarity["tag"]}), // idk why TS needs this + tierColumn({accessorFn: tool => BitCraftTables.ItemDesc.indexedBy("id")().get(tool.itemId)?.tier ?? -1}), + rarityColumn({accessorFn: tool => BitCraftTables.ItemDesc.indexedBy("id")().get(tool.itemId)?.rarity.tag ?? Rarity.Default.tag as Rarity["tag"]}), // idk why TS needs this rowActions({accessorKey: "itemId"}, "item"), ], facetedFilters: [ diff --git a/frontend/src/lib/table-defs/item-list-table.tsx b/frontend/src/lib/table-defs/item-list-table.tsx index d741408..f427732 100644 --- a/frontend/src/lib/table-defs/item-list-table.tsx +++ b/frontend/src/lib/table-defs/item-list-table.tsx @@ -33,7 +33,7 @@ export const ItemListDescDefs: BitCraftToDataDef = { const matchedLoot = lootDescs().get(list.id); if (matchedLoot) { const enemyIndex = BitCraftTables.EnemyDesc.indexedBy("enemyType"); - const matchedEnemy = enemyIndex()?.get(matchedLoot.enemyTypeId); + const matchedEnemy = enemyIndex().get(matchedLoot.enemyTypeId); if (matchedEnemy) return } return <>; @@ -56,7 +56,7 @@ export const ItemListDescDefs: BitCraftToDataDef = { const matchedLoot = lootDescs().get(props.row.original.id); if (matchedLoot) { const enemyIndex = BitCraftTables.EnemyDesc.indexedBy("enemyType"); - const matchedEnemy = enemyIndex()?.get(matchedLoot.enemyTypeId); + const matchedEnemy = enemyIndex().get(matchedLoot.enemyTypeId); if (matchedEnemy) originalIcon = ; } } diff --git a/frontend/src/lib/table-defs/item-table.tsx b/frontend/src/lib/table-defs/item-table.tsx index 648130b..e2885fc 100644 --- a/frontend/src/lib/table-defs/item-table.tsx +++ b/frontend/src/lib/table-defs/item-table.tsx @@ -29,25 +29,25 @@ function getConsolidatedItemStats(item: ItemDesc): CsvStatEntry[] | undefined { const allStats: CsvStatEntry[] = []; // Equipment stats - const equip = BitCraftTables.EquipmentDesc.indexedBy("itemId")?.()?.get(item.id); + const equip = BitCraftTables.EquipmentDesc.indexedBy("itemId")().get(item.id); if (equip?.stats?.length) { allStats.push(...equip.stats); } // Food buff stats - const food = BitCraftTables.FoodDesc.indexedBy("itemId")?.()?.get(item.id); + const food = BitCraftTables.FoodDesc.indexedBy("itemId")().get(item.id); if (food?.buffs?.length) { - const buffIdx = BitCraftTables.BuffDesc.indexedBy("id"); + const buffIdx = BitCraftTables.BuffDesc.indexedBy("id")(); food.buffs.forEach(b => { - const buff = buffIdx?.()?.get(b.buffId); + const buff = buffIdx.get(b.buffId); if (buff?.stats?.length) allStats.push(...buff.stats); }); } // Knowledge stat modifiers (via scroll -> secondary knowledge) - const scroll = BitCraftTables.KnowledgeScrollDesc.indexedBy("itemId")?.()?.get(item.id); + const scroll = BitCraftTables.KnowledgeScrollDesc.indexedBy("itemId")().get(item.id); if (scroll) { - const mod = BitCraftTables.KnowledgeStatModifierDesc.indexedBy("secondaryKnowledgeId")?.()?.get(scroll.secondaryKnowledgeId); + const mod = BitCraftTables.KnowledgeStatModifierDesc.indexedBy("secondaryKnowledgeId")().get(scroll.secondaryKnowledgeId); if (mod?.stats?.length) allStats.push(...mod.stats); } diff --git a/frontend/src/lib/table-defs/knowledge-table.tsx b/frontend/src/lib/table-defs/knowledge-table.tsx index a405594..ce8d5c5 100644 --- a/frontend/src/lib/table-defs/knowledge-table.tsx +++ b/frontend/src/lib/table-defs/knowledge-table.tsx @@ -11,43 +11,43 @@ export const KnowledgeDefs: BitCraftToDataDef = { route: k => ["knowledge", k.id], prefixElement: k => { const scrollIdx = BitCraftTables.KnowledgeScrollDesc.indexedBy("secondaryKnowledgeId"); - const scroll = scrollIdx()?.get(k.id); + const scroll = scrollIdx().get(k.id); if (!scroll) return <>; - const item = BitCraftTables.ItemDesc.indexedBy("id")?.()?.get(scroll.itemId); + const item = BitCraftTables.ItemDesc.indexedBy("id")().get(scroll.itemId); return item ? : <>; }, }), tagColumn(undefined, { accessorFn: row => { - const scroll = BitCraftTables.KnowledgeScrollDesc.indexedBy("secondaryKnowledgeId")()?.get(row.id); + const scroll = BitCraftTables.KnowledgeScrollDesc.indexedBy("secondaryKnowledgeId")().get(row.id); return scroll?.tag ?? ""; } }), tagColumn("Title", { accessorFn: row => { - const scroll = BitCraftTables.KnowledgeScrollDesc.indexedBy("secondaryKnowledgeId")()?.get(row.id); + const scroll = BitCraftTables.KnowledgeScrollDesc.indexedBy("secondaryKnowledgeId")().get(row.id); return scroll?.title ?? ""; } }), boolColumn("Known By Default", { accessorFn: row => { - const scroll = BitCraftTables.KnowledgeScrollDesc.indexedBy("secondaryKnowledgeId")()?.get(row.id); + const scroll = BitCraftTables.KnowledgeScrollDesc.indexedBy("secondaryKnowledgeId")().get(row.id); return scroll?.knownByDefault; } }), boolColumn("Auto Collect", { accessorFn: row => { - const scroll = BitCraftTables.KnowledgeScrollDesc.indexedBy("secondaryKnowledgeId")()?.get(row.id); + const scroll = BitCraftTables.KnowledgeScrollDesc.indexedBy("secondaryKnowledgeId")().get(row.id); return scroll?.autoCollect; } }), statsColumn(undefined, { accessorFn: row => { - const mod = BitCraftTables.KnowledgeStatModifierDesc.indexedBy("secondaryKnowledgeId")()?.get(row.id); + const mod = BitCraftTables.KnowledgeStatModifierDesc.indexedBy("secondaryKnowledgeId")().get(row.id); return mod?.stats.length ? mod?.stats : undefined; } }), - rowActions(undefined, "know", undefined, { accessorFn: k => BitCraftTables.KnowledgeScrollDesc.indexedBy("secondaryKnowledgeId")()?.get(k.id)?.itemId?.toString() }), + rowActions(undefined, "know", undefined, { accessorFn: k => BitCraftTables.KnowledgeScrollDesc.indexedBy("secondaryKnowledgeId")().get(k.id)?.itemId?.toString() }), ], facetedFilters: [ uniqueValuesFilter("Tag"), diff --git a/frontend/src/lib/table-defs/prospecting-table.tsx b/frontend/src/lib/table-defs/prospecting-table.tsx index 013721a..b83015f 100644 --- a/frontend/src/lib/table-defs/prospecting-table.tsx +++ b/frontend/src/lib/table-defs/prospecting-table.tsx @@ -18,17 +18,17 @@ export const ProspectingDefs: BitCraftToDataDef = { { id: "Biomes", accessorFn: (row) => { - const idx = BitCraftTables.BiomeDesc.indexedBy("biomeType"); - return row.biomeRequirements?.map((id) => idx?.()?.get(id)?.name ?? `#${id}`) || []; + const idx = BitCraftTables.BiomeDesc.indexedBy("biomeType", true); + return row.biomeRequirements?.map((id) => idx().get(id)?.name ?? `#${id}`) || []; }, cell: (props) => { const row = props.row.original; if (!row.biomeRequirements?.length) return <>; - const idx = BitCraftTables.BiomeDesc.indexedBy("biomeType"); + const idx = BitCraftTables.BiomeDesc.indexedBy("biomeType", true); return ( {row.biomeRequirements.map(biomeType => { - const biome = idx?.()?.get(biomeType); + const biome = idx().get(biomeType); return ; })} diff --git a/frontend/src/lib/table-defs/quests-table.tsx b/frontend/src/lib/table-defs/quests-table.tsx index 50f289a..fd69994 100644 --- a/frontend/src/lib/table-defs/quests-table.tsx +++ b/frontend/src/lib/table-defs/quests-table.tsx @@ -37,34 +37,34 @@ export function reqOrRewardTagLabel(tag: string): string { export function reqOrRewardName(r: QuestEntry): string { switch (r.tag) { case "QuestChain": - return BitCraftTables.QuestChainDesc.indexedBy("id")()?.get(r.value as number)?.name ?? `Quest #${r.value}`; + return BitCraftTables.QuestChainDesc.indexedBy("id")().get(r.value as number)?.name ?? `Quest #${r.value}`; case "Achievement": - return BitCraftTables.AchievementDesc.indexedBy("id")()?.get(r.value as number)?.name ?? `Achievement #${r.value}`; + return BitCraftTables.AchievementDesc.indexedBy("id")().get(r.value as number)?.name ?? `Achievement #${r.value}`; case "Collectible": - return BitCraftTables.CollectibleDesc.indexedBy("id")()?.get(r.value as number)?.name ?? `Collectible #${r.value}`; + return BitCraftTables.CollectibleDesc.indexedBy("id")().get(r.value as number)?.name ?? `Collectible #${r.value}`; case "Level": { const lr = r.value as LevelRequirement; - return `${BitCraftTables.SkillDesc.indexedBy("id")()?.get(lr.skillId)?.name ?? "Skill"} Lv. ${lr.level}`; + return `${BitCraftTables.SkillDesc.indexedBy("id")().get(lr.skillId)?.name ?? "Skill"} Lv. ${lr.level}`; } case "Experience": { const e = r.value as { skillId: number; quantity: number }; - return `${BitCraftTables.SkillDesc.indexedBy("id")()?.get(e.skillId)?.name ?? "Skill"}: ${fixFloat(e.quantity)}`; + return `${BitCraftTables.SkillDesc.indexedBy("id")().get(e.skillId)?.name ?? "Skill"}: ${fixFloat(e.quantity)}`; } case "ItemStack": { // CompletionCondition.ItemStack wraps ItemStackCompletionCondition; QuestRequirement/QuestReward wraps ItemStack directly const raw = r.value; const stack: ItemStack = "itemStack" in raw ? (raw as ItemStackCompletionCondition).itemStack : raw as ItemStack; if (stack.itemType.tag === ItemType.Cargo.tag) { - return BitCraftTables.CargoDesc.indexedBy("id")()?.get(stack.itemId)?.name ?? `Cargo #${stack.itemId}`; + return BitCraftTables.CargoDesc.indexedBy("id")().get(stack.itemId)?.name ?? `Cargo #${stack.itemId}`; } - return BitCraftTables.ItemDesc.indexedBy("id")()?.get(stack.itemId)?.name ?? `Item #${stack.itemId}`; + return BitCraftTables.ItemDesc.indexedBy("id")().get(stack.itemId)?.name ?? `Item #${stack.itemId}`; } case "EquippedItem": { const id = r.value as number; - return BitCraftTables.ItemDesc.indexedBy("id")()?.get(id)?.name ?? `Item #${id}`; + return BitCraftTables.ItemDesc.indexedBy("id")().get(id)?.name ?? `Item #${id}`; } case "SecondaryKnowledge": - return BitCraftTables.SecondaryKnowledgeDesc.indexedBy("id")()?.get(r.value as number)?.name ?? `Knowledge #${r.value}`; + return BitCraftTables.SecondaryKnowledgeDesc.indexedBy("id")().get(r.value as number)?.name ?? `Knowledge #${r.value}`; default: return ""; } @@ -77,7 +77,7 @@ export function ReqOrRewardLink(props: { qr: QuestEntry }) { return ; case "Achievement": { const id = props.qr.value as number; - const ach = () => BitCraftTables.AchievementDesc.indexedBy("id")()?.get(id); + const ach = () => BitCraftTables.AchievementDesc.indexedBy("id")().get(id); return ; } case "Collectible": diff --git a/frontend/src/lib/table-defs/tools-table.tsx b/frontend/src/lib/table-defs/tools-table.tsx index e96e3b7..61bb57d 100644 --- a/frontend/src/lib/table-defs/tools-table.tsx +++ b/frontend/src/lib/table-defs/tools-table.tsx @@ -10,22 +10,22 @@ export const ToolDefs: BitCraftToDataDef = { columns: [ headerColumn({ title: "Name", - accessor: {accessorFn: tool => BitCraftTables.ItemDesc.indexedBy("id")?.()?.get(tool.itemId)?.name ?? `Item #${tool.itemId}`}, + accessor: {accessorFn: tool => BitCraftTables.ItemDesc.indexedBy("id")().get(tool.itemId)?.name ?? `Item #${tool.itemId}`}, route: tool => ["tool", tool.itemId], prefixElement: tool => { - const item = BitCraftTables.ItemDesc.indexedBy("id")?.()?.get(tool.itemId); + const item = BitCraftTables.ItemDesc.indexedBy("id")().get(tool.itemId); return item ? : <>; }, }), { id: "Tool Type", - accessorFn: row => BitCraftTables.ToolTypeDesc.indexedBy("id")?.()?.get(row.toolType)?.name ?? `#${row.toolType}`, + accessorFn: row => BitCraftTables.ToolTypeDesc.indexedBy("id")().get(row.toolType)?.name ?? `#${row.toolType}`, filterFn: includedIn(), }, {id: "Power", accessorKey: "power", filterFn: "inNumberRange"}, {id: "Level", accessorKey: "level", filterFn: "inNumberRange"}, - tierColumn({accessorFn: tool => BitCraftTables.ItemDesc.indexedBy("id")?.().get(tool.itemId)?.tier ?? -1}), - rarityColumn({accessorFn: tool => BitCraftTables.ItemDesc.indexedBy("id")?.().get(tool.itemId)?.rarity.tag ?? Rarity.Default.tag as Rarity["tag"]}), // idk why TS needs this + tierColumn({accessorFn: tool => BitCraftTables.ItemDesc.indexedBy("id")().get(tool.itemId)?.tier ?? -1}), + rarityColumn({accessorFn: tool => BitCraftTables.ItemDesc.indexedBy("id")().get(tool.itemId)?.rarity.tag ?? Rarity.Default.tag as Rarity["tag"]}), // idk why TS needs this rowActions({accessorKey: "itemId"}, "item"), ], facetedFilters: [ diff --git a/frontend/src/lib/table-defs/traveler-tasks-table.tsx b/frontend/src/lib/table-defs/traveler-tasks-table.tsx index 0866b88..6bbc359 100644 --- a/frontend/src/lib/table-defs/traveler-tasks-table.tsx +++ b/frontend/src/lib/table-defs/traveler-tasks-table.tsx @@ -45,7 +45,7 @@ export const TravelerTaskDefs: BitCraftToDataDef = { { id: "Skill", accessorFn: task => { - const skillIndex = BitCraftTables.SkillDesc.indexedBy("id")!()!; + const skillIndex = BitCraftTables.SkillDesc.indexedBy("id")(); if (!task.levelRequirement.skillId) return ""; const skillData = skillIndex.get(task.levelRequirement.skillId); return skillData ? skillData.name : "Unknown"; diff --git a/frontend/src/lib/table-defs/traveler-trades-table.tsx b/frontend/src/lib/table-defs/traveler-trades-table.tsx index 84c5a16..1102ffe 100644 --- a/frontend/src/lib/table-defs/traveler-trades-table.tsx +++ b/frontend/src/lib/table-defs/traveler-trades-table.tsx @@ -47,7 +47,7 @@ export const TravelerTradeDefs: BitCraftToDataDef = { accessorFn: (trade) => { const req = trade.levelRequirements[0]; if (!req?.skillId) return undefined; - return BitCraftTables.SkillDesc.indexedBy("id")()?.get(req.skillId)?.name ?? `#${req.skillId}`; + return BitCraftTables.SkillDesc.indexedBy("id")().get(req.skillId)?.name ?? `#${req.skillId}`; }, cell: (props) => { const req = props.row.original.levelRequirements[0]; diff --git a/frontend/src/lib/table-defs/weapon-table.tsx b/frontend/src/lib/table-defs/weapon-table.tsx index 8cb44fe..5726d9c 100644 --- a/frontend/src/lib/table-defs/weapon-table.tsx +++ b/frontend/src/lib/table-defs/weapon-table.tsx @@ -10,20 +10,20 @@ export const WeaponDefs: BitCraftToDataDef = { columns: [ headerColumn({ title: "Name", - accessor: {accessorFn: wep => BitCraftTables.ItemDesc.indexedBy("id")?.()?.get(wep.itemId)?.name ?? `Item #${wep.itemId}`}, + accessor: {accessorFn: wep => BitCraftTables.ItemDesc.indexedBy("id")().get(wep.itemId)?.name ?? `Item #${wep.itemId}`}, route: wep => ["weapon", wep.itemId], prefixElement: wep => { - const item = (() => BitCraftTables.ItemDesc.indexedBy("id")?.()?.get(wep.itemId))(); + const item = (() => BitCraftTables.ItemDesc.indexedBy("id")().get(wep.itemId))(); return item ? : <>; }, }), { id: "Weapon Type", - accessorFn: (row) => BitCraftTables.WeaponTypeDesc.indexedBy("id")?.()?.get(row.weaponType)?.name ?? `#${row.weaponType}`, + accessorFn: (row) => BitCraftTables.WeaponTypeDesc.indexedBy("id")().get(row.weaponType)?.name ?? `#${row.weaponType}`, filterFn: includedIn() }, tierColumn(), - rarityColumn({accessorFn: (row) => BitCraftTables.ItemDesc.indexedBy("id")?.()?.get(row.itemId)?.rarity.tag ?? Rarity.Default.tag as Rarity["tag"]}), + rarityColumn({accessorFn: (row) => BitCraftTables.ItemDesc.indexedBy("id")().get(row.itemId)?.rarity.tag ?? Rarity.Default.tag as Rarity["tag"]}), {id: "Min Damage", accessorKey: "minDamage", filterFn: "inNumberRange"}, {id: "Max Damage", accessorKey: "maxDamage", filterFn: "inNumberRange"}, { diff --git a/frontend/src/lib/table-utils/column-builders.tsx b/frontend/src/lib/table-utils/column-builders.tsx index 21466b3..fc03f05 100644 --- a/frontend/src/lib/table-utils/column-builders.tsx +++ b/frontend/src/lib/table-utils/column-builders.tsx @@ -81,7 +81,7 @@ export function knowledgeColumn( const ids = resolveAccessor(accessor, row); if (!ids?.length) return []; const idx = BitCraftTables.SecondaryKnowledgeDesc.indexedBy("id")(); - return ids.map(id => idx?.get(id)?.name ?? `#${id}`); + return ids.map(id => idx.get(id)?.name ?? `#${id}`); }; return { id: title ?? "Required Knowledge", diff --git a/frontend/src/routes/database/achievement/[id].tsx b/frontend/src/routes/database/achievement/[id].tsx index 077b3dc..6d222e3 100644 --- a/frontend/src/routes/database/achievement/[id].tsx +++ b/frontend/src/routes/database/achievement/[id].tsx @@ -17,7 +17,7 @@ export default function AchievementDetail() { const achievement = createMemo(() => { const id = parseInt(params.id as string ?? "", 10); if (isNaN(id)) return undefined; - return index()?.get(id); + return index().get(id); }); const prereqs = createMemo(() => { @@ -55,7 +55,7 @@ export default function AchievementDetail() { description={achievement()?.description} tag={`${achievement()?.pointsReward} points`} rawData={achievement()} - spacetimeTable={BitCraftTables.AchievementDesc.st_name} + spacetimeTable={BitCraftTables.AchievementDesc.spacetimeName} objectId={achievement()?.id} tabs={[ {id: "prereqs", label: "Prerequisites", count: prereqs().length, content: () => }, diff --git a/frontend/src/routes/database/biome/[id].tsx b/frontend/src/routes/database/biome/[id].tsx index 494e0f3..9922401 100644 --- a/frontend/src/routes/database/biome/[id].tsx +++ b/frontend/src/routes/database/biome/[id].tsx @@ -1,4 +1,4 @@ -import {useNavigate, useParams} from "@solidjs/router"; +import {useParams} from "@solidjs/router"; import {createMemo} from "solid-js"; import {ProspectingDesc} from "~/bindings/src/prospecting_desc_type"; import {FontIcon} from "~/components/icons/font-icons"; @@ -9,14 +9,13 @@ import {BitCraftTables, useTablesLoading} from "~/lib/spacetime"; export default function BiomeDetail() { const params = useParams(); - const navigate = useNavigate(); const isLoading = useTablesLoading(BitCraftTables.BiomeDesc); - const index = BitCraftTables.BiomeDesc.indexedBy("biomeType"); + const index = BitCraftTables.BiomeDesc.indexedBy("biomeType", true); const biome = createMemo(() => { const id = parseInt(params.id as string ?? "", 10); if (isNaN(id)) return undefined; - return index()?.get(id); + return index().get(id); }); const prospectingEntries = createMemo(() => { @@ -37,7 +36,7 @@ export default function BiomeDetail() { {label: "Disallow Player Build", value: biome()?.disallowPlayerBuild}, ]} rawData={biome()} - spacetimeTable={BitCraftTables.BiomeDesc.st_name} + spacetimeTable={BitCraftTables.BiomeDesc.spacetimeName} objectId={biome()?.biomeType} tabs={[ { diff --git a/frontend/src/routes/database/buff/[id].tsx b/frontend/src/routes/database/buff/[id].tsx index ee4999f..45bb5da 100644 --- a/frontend/src/routes/database/buff/[id].tsx +++ b/frontend/src/routes/database/buff/[id].tsx @@ -16,13 +16,13 @@ export default function BuffDetail() { const buff = createMemo(() => { const id = parseInt(params.id as string ?? "", 10); if (isNaN(id)) return undefined; - return buffIndex()?.get(id); + return buffIndex().get(id); }); const buffType = createMemo(() => { const b = buff(); if (!b) return undefined; - return BitCraftTables.BuffTypeDesc.indexedBy("id")?.()?.get(b.buffTypeId); + return BitCraftTables.BuffTypeDesc.indexedBy("id")().get(b.buffTypeId); }); const details = createMemo((): DetailGroup[] => { @@ -65,7 +65,7 @@ export default function BuffDetail() { const itemIdx = BitCraftTables.ItemDesc.indexedBy("id")(); for (const food of BitCraftTables.FoodDesc.get() ?? []) { if (food.buffs.some(e => e.buffId === b.id)) { - const item = itemIdx?.get(food.itemId); + const item = itemIdx.get(food.itemId); entries.push({ href: `/database/food/${food.itemId}`, iconPage: "Food", @@ -76,7 +76,7 @@ export default function BuffDetail() { const buildingIdx = BitCraftTables.BuildingDesc.indexedBy("id")(); for (const bb of BitCraftTables.BuildingBuffDesc.get() ?? []) { if (bb.buffs.some(e => e.buffId === b.id)) { - const building = buildingIdx?.get(bb.buildingId); + const building = buildingIdx.get(bb.buildingId); entries.push({ href: `/database/building/${bb.buildingId}`, iconPage: "Structures", @@ -97,7 +97,7 @@ export default function BuffDetail() { tag={buffType()?.name} details={details()} rawData={buff()} - spacetimeTable={BitCraftTables.BuffDesc.st_name} + spacetimeTable={BitCraftTables.BuffDesc.spacetimeName} objectId={buff()?.id} tabs={[ { diff --git a/frontend/src/routes/database/building/[id].tsx b/frontend/src/routes/database/building/[id].tsx index 764c5b4..ff1b000 100644 --- a/frontend/src/routes/database/building/[id].tsx +++ b/frontend/src/routes/database/building/[id].tsx @@ -21,7 +21,7 @@ export default function BuildingDetail() { const building = createMemo(() => { const id = parseInt(params.id as string ?? "", 10); if (isNaN(id)) return undefined; - return buildingIndex()?.get(id); + return buildingIndex().get(id); }); const tier = createMemo(() => building() ? getBuildingTier(building()!) : undefined); @@ -55,7 +55,7 @@ export default function BuildingDetail() { if (!b) return []; const idx = buildingTypeIndex(); return b.functions.map(f => { - const bt = idx?.get(f.functionType); + const bt = idx.get(f.functionType); return bt ? `${bt.name} Lvl ${f.level}` : `#${f.functionType} Lvl ${f.level}`; }); }); @@ -86,7 +86,7 @@ export default function BuildingDetail() { } b.functions.forEach(f => { - const bt = buildingTypeIndex()?.get(f.functionType); + const bt = buildingTypeIndex().get(f.functionType); const funcName = bt?.name ?? `Function #${f.functionType}`; const funcProps: DetailProperty[] = []; if (f.craftingSlots > 0) funcProps.push({label: "Crafting Slots", value: f.craftingSlots}); @@ -122,7 +122,7 @@ export default function BuildingDetail() { description={building()?.description} details={detailGroups()} rawData={building()} - spacetimeTable={BitCraftTables.BuildingDesc.st_name} + spacetimeTable={BitCraftTables.BuildingDesc.spacetimeName} objectId={building()?.id} chatLink={`(build=${building()?.id})`} tabs={[ diff --git a/frontend/src/routes/database/cargo/[id].tsx b/frontend/src/routes/database/cargo/[id].tsx index 1a67453..e8e069d 100644 --- a/frontend/src/routes/database/cargo/[id].tsx +++ b/frontend/src/routes/database/cargo/[id].tsx @@ -58,13 +58,13 @@ export default function CargoDetail() { const cargo = createMemo(() => { const id = parseInt(params.id as string ?? "", 10); if (isNaN(id)) return undefined; - return cargoIndex()?.get(id); + return cargoIndex().get(id); }); const knowledgeName = createMemo(() => { const c = cargo(); if (!c?.secondaryKnowledgeId) return undefined; - return knowledgeIndex()?.get(c.secondaryKnowledgeId)?.name; + return knowledgeIndex().get(c.secondaryKnowledgeId)?.name; }); // ─── Per-type recipe/relationship finders ─────────────────── @@ -125,7 +125,7 @@ export default function CargoDetail() { {label: "Knowledge", value: knowledgeName()}, ]} rawData={cargo()} - spacetimeTable={BitCraftTables.CargoDesc.st_name} + spacetimeTable={BitCraftTables.CargoDesc.spacetimeName} objectId={cargo()?.id} chatLink={`(cargo=${cargo()?.id})`} tabs={!cargo() ? [] : [ diff --git a/frontend/src/routes/database/claim-research/[id].tsx b/frontend/src/routes/database/claim-research/[id].tsx index eec769f..79268af 100644 --- a/frontend/src/routes/database/claim-research/[id].tsx +++ b/frontend/src/routes/database/claim-research/[id].tsx @@ -15,14 +15,13 @@ export default function ClaimResearchDetail() { const tech = createMemo(() => { const id = parseInt(params.id as string ?? "", 10); if (isNaN(id)) return undefined; - return index()?.get(id); + return index().get(id); }); const requirements = createMemo(() => { const t = tech(); if (!t?.requirements?.length) return []; const idx = index(); - if (!idx) return []; return t.requirements.map(id => idx.get(id)).filter((v): v is ClaimTechDesc => !!v); }); @@ -30,7 +29,6 @@ export default function ClaimResearchDetail() { const t = tech(); if (!t?.unlocksTechs?.length) return []; const idx = index(); - if (!idx) return []; return t.unlocksTechs.map(id => idx.get(id)).filter((v): v is ClaimTechDesc => !!v); }); @@ -67,7 +65,7 @@ export default function ClaimResearchDetail() { } ]} rawData={tech()} - spacetimeTable={BitCraftTables.ClaimTechDesc.st_name} + spacetimeTable={BitCraftTables.ClaimTechDesc.spacetimeName} objectId={tech()?.id} tabs={[ { diff --git a/frontend/src/routes/database/collectible/[id].tsx b/frontend/src/routes/database/collectible/[id].tsx index f82fe45..cd91c8a 100644 --- a/frontend/src/routes/database/collectible/[id].tsx +++ b/frontend/src/routes/database/collectible/[id].tsx @@ -22,20 +22,19 @@ export default function CollectibleDetail() { const collectible = createMemo(() => { const id = parseInt(params.id as string ?? "", 10); if (isNaN(id)) return undefined; - return index()?.get(id); + return index().get(id); }); const itemDeed = createMemo(() => { const c = collectible(); if (!c?.itemDeedId) return undefined; - return itemIndex()?.get(c.itemDeedId); + return itemIndex().get(c.itemDeedId); }); const knowledgesToUse = createMemo(() => { const c = collectible(); if (!c?.requiredKnowledgesToUse?.length) return []; const idx = knowledgeIndex(); - if (!idx) return []; return c.requiredKnowledgesToUse.map(id => idx.get(id)).filter((v): v is SecondaryKnowledgeDesc => !!v); }); @@ -43,7 +42,6 @@ export default function CollectibleDetail() { const c = collectible(); if (!c?.requiredKnowledgesToConvert?.length) return []; const idx = knowledgeIndex(); - if (!idx) return []; return c.requiredKnowledgesToConvert.map(id => idx.get(id)).filter((v): v is SecondaryKnowledgeDesc => !!v); }); @@ -66,7 +64,7 @@ export default function CollectibleDetail() { if (!c) return []; if (c.collectibleType.tag === "DeployableAppearanceOverride") { const appearances = BitCraftTables.DeployableAppearanceOverrideDesc.indexedBy("collectibleId")(); - const appearance = appearances?.get(c.id); + const appearance = appearances.get(c.id); if (!appearance) return []; const model = appearance.affectedModelAddress; const deployables = BitCraftTables.DeployableDesc.get(); @@ -75,7 +73,7 @@ export default function CollectibleDetail() { .filter((p): p is [DeployableDesc, CollectibleDesc] => !!p[1]) ?? []; } else if (c.collectibleType.tag === "Deployable") { const deployables = BitCraftTables.DeployableDesc.indexedBy("deployFromCollectibleId"); - const dep = deployables()?.get(c.id); + const dep = deployables().get(c.id); return dep ? [[dep, c]] as [DeployableDesc, CollectibleDesc][] : []; } else { return []; @@ -101,7 +99,7 @@ export default function CollectibleDetail() { {label: "Max Equip Count", value: collectible()?.maxEquipCount}, ]} rawData={collectible()} - spacetimeTable={BitCraftTables.CollectibleDesc.st_name} + spacetimeTable={BitCraftTables.CollectibleDesc.spacetimeName} objectId={collectible()?.id} chatLink={`(col=${collectible()?.id})`} tabs={[ diff --git a/frontend/src/routes/database/combat/[id].tsx b/frontend/src/routes/database/combat/[id].tsx index 3a9d418..ed3cd76 100644 --- a/frontend/src/routes/database/combat/[id].tsx +++ b/frontend/src/routes/database/combat/[id].tsx @@ -19,20 +19,18 @@ export default function CombatDetail() { const action = createMemo(() => { const id = parseInt(params.id as string ?? "", 10); if (isNaN(id)) return undefined; - return index()?.get(id); + return index().get(id); }); const weaponTypes = createMemo(() => { const a = action(); if (!a?.weaponTypeRequirements?.length) return []; const idx = weaponTypeIndex(); - if (!idx) return []; return a.weaponTypeRequirements.map(id => idx.get(id)).filter((v): v is WeaponTypeDesc => !!v); }); const mapBuffEffects = (effects: BuffEffect[]) => { const idx = buffIndex(); - if (!idx) return []; return effects.map((be) => { let buffDesc = idx.get(be.buffId); return ({ @@ -104,7 +102,7 @@ export default function CombatDetail() { ]} rawData={action()} - spacetimeTable={BitCraftTables.CombatActionDesc.st_name} + spacetimeTable={BitCraftTables.CombatActionDesc.spacetimeName} objectId={action()?.id} tabs={[ {id: "self-buffs", label: "Self Buffs", count: selfBuffs().length, content: () => }, diff --git a/frontend/src/routes/database/creature/[id].tsx b/frontend/src/routes/database/creature/[id].tsx index e08aa8e..ddfde3b 100644 --- a/frontend/src/routes/database/creature/[id].tsx +++ b/frontend/src/routes/database/creature/[id].tsx @@ -29,16 +29,15 @@ export default function CreatureDetail() { const creature = createMemo(() => { const id = parseInt(params.id as string ?? "", 10); if (isNaN(id)) return undefined; - return index()?.get(id); + return index().get(id); }); - const pathfinding = createMemo(() => creature() ? pathfindingIndex()?.get(creature()!.pathfindingId) : undefined); + const pathfinding = createMemo(() => creature() ? pathfindingIndex().get(creature()!.pathfindingId) : undefined); const combatActions = createMemo(() => { const c = creature(); if (!c?.combatActionsIds?.length) return []; const idx = combatActionIndex(); - if (!idx) return []; return c.combatActionsIds.map(id => idx.get(id)).filter((v): v is CombatActionDesc => !!v); }); @@ -130,7 +129,7 @@ export default function CreatureDetail() { tag={creature()?.tag} details={detailGroups()} rawData={creature()} - spacetimeTable={BitCraftTables.EnemyDesc.st_name} + spacetimeTable={BitCraftTables.EnemyDesc.spacetimeName} objectId={creature()?.enemyType} chatLink={`(mob=${creature()?.enemyType})`} tabs={[ diff --git a/frontend/src/routes/database/deployable/[id].tsx b/frontend/src/routes/database/deployable/[id].tsx index 202e88c..2611766 100644 --- a/frontend/src/routes/database/deployable/[id].tsx +++ b/frontend/src/routes/database/deployable/[id].tsx @@ -21,20 +21,19 @@ export default function DeployableDetail() { const deployable = createMemo(() => { const id = parseInt(params.id as string ?? "", 10); if (isNaN(id)) return undefined; - return index()?.get(id); + return index().get(id); }); - const collectible = createMemo(() => deployable() ? collectibleIndex()?.get(deployable()!.deployFromCollectibleId) : undefined); - const itemDeed = createMemo(() => collectible() ? itemIndex()?.get(collectible()!.itemDeedId) : undefined); - const pathfinding = createMemo(() => deployable() ? pathfindingIndex()?.get(deployable()!.pathfindingId) : undefined); + const collectible = createMemo(() => deployable() ? collectibleIndex().get(deployable()!.deployFromCollectibleId) : undefined); + const itemDeed = createMemo(() => collectible() ? itemIndex().get(collectible()!.itemDeedId) : undefined); + const pathfinding = createMemo(() => deployable() ? pathfindingIndex().get(deployable()!.pathfindingId) : undefined); const appearances = createMemo(() => { const dep = deployable(); if (!dep) return []; const model = dep.modelAddress; const overrides = BitCraftTables.DeployableAppearanceOverrideDesc.get(); - const collectibles = BitCraftTables.CollectibleDesc.indexedBy("id")(); return overrides?.filter(o => o.affectedModelAddress === model) - .map(dao => collectibles.get(dao.collectibleId)) + .map(dao => collectibleIndex().get(dao.collectibleId)) .filter((col): col is CollectibleDesc => !!col) ?? []; }); @@ -102,7 +101,7 @@ export default function DeployableDetail() { tag={deployable()?.deployableType?.tag} details={detailGroups()} rawData={deployable()} - spacetimeTable={BitCraftTables.DeployableDesc.st_name} + spacetimeTable={BitCraftTables.DeployableDesc.spacetimeName} objectId={deployable()?.id} chatLink={`(col=${collectible()?.id})`} tabs={[ diff --git a/frontend/src/routes/database/equipment/[id].tsx b/frontend/src/routes/database/equipment/[id].tsx index c11d883..633e02e 100644 --- a/frontend/src/routes/database/equipment/[id].tsx +++ b/frontend/src/routes/database/equipment/[id].tsx @@ -15,10 +15,10 @@ export default function EquipmentDetail() { const equipment = createMemo(() => { const id = parseInt(params.id as string ?? "", 10); if (isNaN(id)) return undefined; - return eqIndex()?.get(id); + return eqIndex().get(id); }); - const item = createMemo(() => equipment() ? itemIndex()?.get(equipment()!.itemId) : undefined); + const item = createMemo(() => equipment() ? itemIndex().get(equipment()!.itemId) : undefined); const details = createMemo((): DetailGroup[] => { const eq = equipment(); @@ -58,7 +58,7 @@ export default function EquipmentDetail() { rarity={item()?.rarity?.tag} details={details()} rawData={equipment()} - spacetimeTable={BitCraftTables.EquipmentDesc.st_name} + spacetimeTable={BitCraftTables.EquipmentDesc.spacetimeName} objectId={equipment()?.itemId} chatLink={`(item=${item()?.id})`} tabs={[ diff --git a/frontend/src/routes/database/food/[id].tsx b/frontend/src/routes/database/food/[id].tsx index cb50a58..7e033bc 100644 --- a/frontend/src/routes/database/food/[id].tsx +++ b/frontend/src/routes/database/food/[id].tsx @@ -17,17 +17,17 @@ export default function FoodDetail() { const food = createMemo(() => { const id = parseInt(params.id as string ?? "", 10); if (isNaN(id)) return undefined; - return foodIndex()?.get(id); + return foodIndex().get(id); }); - const item = createMemo(() => food() ? itemIndex()?.get(food()!.itemId) : undefined); + const item = createMemo(() => food() ? itemIndex().get(food()!.itemId) : undefined); const buffs = createMemo(() => { const f = food(); if (!f?.buffs?.length) return []; - const buffIdx = BitCraftTables.BuffDesc.indexedBy("id"); + const buffIdx = BitCraftTables.BuffDesc.indexedBy("id")(); return f.buffs.map(b => { - const buff = buffIdx()?.get(b.buffId); + const buff = buffIdx.get(b.buffId); return { ...b, buff, @@ -68,7 +68,7 @@ export default function FoodDetail() { ...statGroups() ]} rawData={food()} - spacetimeTable={BitCraftTables.FoodDesc.st_name} + spacetimeTable={BitCraftTables.FoodDesc.spacetimeName} objectId={food()?.itemId} chatLink={`(item=${item()?.id})`} tabs={[ diff --git a/frontend/src/routes/database/item-list/[id].tsx b/frontend/src/routes/database/item-list/[id].tsx index 6c7bc9f..79b03f5 100644 --- a/frontend/src/routes/database/item-list/[id].tsx +++ b/frontend/src/routes/database/item-list/[id].tsx @@ -16,7 +16,7 @@ export default function ItemListDetail() { const itemList = createMemo(() => { const id = parseInt(params.id as string ?? "", 10); if (isNaN(id)) return undefined; - return index()?.get(id); + return index().get(id); }); const source = createMemo((): ItemListSource => { @@ -43,7 +43,7 @@ export default function ItemListDetail() { tag={"Item List"} icon={{il => }} rawData={itemList()} - spacetimeTable={BitCraftTables.ItemListDesc.st_name} + spacetimeTable={BitCraftTables.ItemListDesc.spacetimeName} objectId={itemList()?.id} chatLink={chatLink()} tabs={[ diff --git a/frontend/src/routes/database/item/[id].tsx b/frontend/src/routes/database/item/[id].tsx index 8f392f6..e44cbba 100644 --- a/frontend/src/routes/database/item/[id].tsx +++ b/frontend/src/routes/database/item/[id].tsx @@ -100,25 +100,25 @@ export default function ItemDetail() { const item = createMemo(() => { const id = parseInt(params.id as string ?? "", 10); if (isNaN(id)) return undefined; - return itemIndex()?.get(id); + return itemIndex().get(id); }); - const toolData = createMemo(() => item() ? toolIndex()?.get(item()!.id) : undefined); - const equipData = createMemo(() => item() ? equipIndex()?.get(item()!.id) : undefined); - const foodData = createMemo(() => item() ? foodIndex()?.get(item()!.id) : undefined); - const weaponData = createMemo(() => item() ? weaponIndex()?.get(item()!.id) : undefined); - const scrollData = createMemo(() => item() ? knowledgeScrollIndex()?.get(item()!.id) : undefined); + const toolData = createMemo(() => item() ? toolIndex().get(item()!.id) : undefined); + const equipData = createMemo(() => item() ? equipIndex().get(item()!.id) : undefined); + const foodData = createMemo(() => item() ? foodIndex().get(item()!.id) : undefined); + const weaponData = createMemo(() => item() ? weaponIndex().get(item()!.id) : undefined); + const scrollData = createMemo(() => item() ? knowledgeScrollIndex().get(item()!.id) : undefined); const collectibleData = createMemo(() => { - const coll = collectibleIndex(); const i = item(); - if (!i || !coll) return []; + if (!i) return []; + const coll = collectibleIndex(); return coll.get(i.id) ?? []; }); const knowledgeStatData = createMemo(() => { const scroll = scrollData(); if (!scroll) return undefined; - return BitCraftTables.KnowledgeStatModifierDesc.indexedBy("secondaryKnowledgeId")()?.get(scroll.secondaryKnowledgeId); + return BitCraftTables.KnowledgeStatModifierDesc.indexedBy("secondaryKnowledgeId")().get(scroll.secondaryKnowledgeId); }); // Expanded food buff info (buff desc + stats) @@ -187,7 +187,7 @@ export default function ItemDetail() { // Tool const tool = toolData(); if (tool) { - const toolType = toolTypeIndex()?.get(tool.toolType); + const toolType = toolTypeIndex().get(tool.toolType); groups.push({ heading: Tool, properties: [ @@ -293,7 +293,7 @@ export default function ItemDetail() { tag={item()?.tag} details={detailGroups()} rawData={item()} - spacetimeTable={BitCraftTables.ItemDesc.st_name} + spacetimeTable={BitCraftTables.ItemDesc.spacetimeName} objectId={item()?.id} chatLink={`(item=${item()?.id})`} summaryContent={placeablePlacements().length === 1 ? () => ( diff --git a/frontend/src/routes/database/knowledge/[id].tsx b/frontend/src/routes/database/knowledge/[id].tsx index 3dca97c..c16cecd 100644 --- a/frontend/src/routes/database/knowledge/[id].tsx +++ b/frontend/src/routes/database/knowledge/[id].tsx @@ -16,25 +16,25 @@ export default function KnowledgeDetail() { const knowledge = createMemo(() => { const id = parseInt(params.id as string ?? "", 10); if (isNaN(id)) return undefined; - return knowledgeIndex()?.get(id); + return knowledgeIndex().get(id); }); const scroll = createMemo(() => { const k = knowledge(); if (!k) return undefined; - return BitCraftTables.KnowledgeScrollDesc.indexedBy("secondaryKnowledgeId")()?.get(k.id); + return BitCraftTables.KnowledgeScrollDesc.indexedBy("secondaryKnowledgeId")().get(k.id); }); const statMod = createMemo(() => { const k = knowledge(); if (!k) return undefined; - return BitCraftTables.KnowledgeStatModifierDesc.indexedBy("secondaryKnowledgeId")()?.get(k.id); + return BitCraftTables.KnowledgeStatModifierDesc.indexedBy("secondaryKnowledgeId")().get(k.id); }); const item = createMemo(() => { const s = scroll(); if (!s) return undefined; - return BitCraftTables.ItemDesc.indexedBy("id")?.()?.get(s.itemId); + return BitCraftTables.ItemDesc.indexedBy("id")().get(s.itemId); }); const questRequires = createMemo(() => { @@ -90,7 +90,7 @@ export default function KnowledgeDetail() { tag={scroll()?.tag} details={details()} rawData={knowledge()} - spacetimeTable={BitCraftTables.SecondaryKnowledgeDesc.st_name} + spacetimeTable={BitCraftTables.SecondaryKnowledgeDesc.spacetimeName} objectId={knowledge()?.id} chatLink={scroll() ? `(know=${scroll()!.itemId})` : undefined} tabs={[ diff --git a/frontend/src/routes/database/paving/[id].tsx b/frontend/src/routes/database/paving/[id].tsx index 6a87ef1..e2d29be 100644 --- a/frontend/src/routes/database/paving/[id].tsx +++ b/frontend/src/routes/database/paving/[id].tsx @@ -19,14 +19,14 @@ export default function PavingDetail() { const tile = createMemo(() => { const id = parseInt(params.id as string ?? "", 10); if (isNaN(id)) return undefined; - return index()?.get(id); + return index().get(id); }); const experienceStr = createMemo(() => { const t = tile(); if (!t?.experiencePerProgress?.length) return undefined; return t.experiencePerProgress.map(exp => { - const name = skillIndex()?.get(exp.skillId)?.name ?? `Skill ${exp.skillId}`; + const name = skillIndex().get(exp.skillId)?.name ?? `Skill ${exp.skillId}`; return `${name}: ${fixFloat(exp.quantity, 4)}`; }).join(", "); }); @@ -35,7 +35,6 @@ export default function PavingDetail() { const t = tile(); if (!t?.requiredKnowledges?.length) return []; const idx = knowledgeIndex(); - if (!idx) return []; return t.requiredKnowledges.map(id => idx.get(id)).filter((v): v is SecondaryKnowledgeDesc => !!v); }); @@ -56,7 +55,7 @@ export default function PavingDetail() { {label: "Build Time", value: tile() ? `${fixFloat(tile()!.pavingDuration)}s` : undefined}, ]} rawData={tile()} - spacetimeTable={BitCraftTables.PavingTileDesc.st_name} + spacetimeTable={BitCraftTables.PavingTileDesc.spacetimeName} objectId={tile()?.id} tabs={[ { diff --git a/frontend/src/routes/database/placeable/[id].tsx b/frontend/src/routes/database/placeable/[id].tsx index afde99e..a146dde 100644 --- a/frontend/src/routes/database/placeable/[id].tsx +++ b/frontend/src/routes/database/placeable/[id].tsx @@ -33,7 +33,7 @@ export default function PlaceableDetail() { const placeable = createMemo(() => { const id = parseInt(params.id as string ?? "", 10); if (isNaN(id)) return undefined; - return placeableIndex()?.get(id); + return placeableIndex().get(id); }); const placeableId = () => placeable()?.id; @@ -194,7 +194,7 @@ export default function PlaceableDetail() {
    ) : undefined} rawData={placeable()} - spacetimeTable={BitCraftTables.PlaceableDesc.st_name} + spacetimeTable={BitCraftTables.PlaceableDesc.spacetimeName} objectId={placeable()?.id} tabs={tabs()} /> diff --git a/frontend/src/routes/database/prospecting/[id].tsx b/frontend/src/routes/database/prospecting/[id].tsx index ae8ae5d..0d45fbd 100644 --- a/frontend/src/routes/database/prospecting/[id].tsx +++ b/frontend/src/routes/database/prospecting/[id].tsx @@ -48,14 +48,14 @@ export default function ProspectingDetail() { const params = useParams(); const isLoading = useTablesLoading(BitCraftTables.ProspectingDesc); const index = BitCraftTables.ProspectingDesc.indexedBy("id"); - const biomeIndex = BitCraftTables.BiomeDesc.indexedBy("biomeType"); + const biomeIndex = BitCraftTables.BiomeDesc.indexedBy("biomeType", true); const enemyIndex = BitCraftTables.EnemyDesc.indexedBy("enemyType"); const enemyParamsIndex = BitCraftTables.EnemyAiParamsDesc.indexedBy("id"); const prospecting = createMemo(() => { const id = parseInt(params.id as string ?? "", 10); if (isNaN(id)) return undefined; - return index()?.get(id); + return index().get(id); }); const rangeStr = (arr: Array | undefined) => { @@ -68,7 +68,6 @@ export default function ProspectingDetail() { const p = prospecting(); if (!p?.biomeRequirements?.length) return []; const idx = biomeIndex(); - if (!idx) return []; return p.biomeRequirements.map(id => idx.get(id)).filter((b): b is NonNullable => !!b); }); @@ -91,11 +90,11 @@ export default function ProspectingDetail() { return {type: "resource" as const, items: matched ?? []}; } if (p.enemyAiDescId > 0) { - const enemyParams = enemyParamsIndex()?.get(p.enemyAiDescId); + const enemyParams = enemyParamsIndex().get(p.enemyAiDescId); if (!enemyParams) return {type: "enemy" as const, items: []}; const tagOrdinal = BitCraftTables.EnemyAiParamsDesc.tagToOrdinal("enemyType"); const ordinal = tagOrdinal.get(enemyParams.enemyType.tag); - const enemy = ordinal !== undefined ? enemyIndex()?.get(ordinal) : undefined; + const enemy = ordinal !== undefined ? enemyIndex().get(ordinal) : undefined; return {type: "enemy" as const, items: enemy ? [enemy] : []}; } return undefined; @@ -126,7 +125,7 @@ export default function ProspectingDetail() { {label: "Allow Aquatic Breadcrumb", value: prospecting()?.allowAquaticBreadCrumb}, ]} rawData={prospecting()} - spacetimeTable={BitCraftTables.ProspectingDesc.st_name} + spacetimeTable={BitCraftTables.ProspectingDesc.spacetimeName} objectId={prospecting()?.id} tabs={[ { diff --git a/frontend/src/routes/database/quest-chain/[id].tsx b/frontend/src/routes/database/quest-chain/[id].tsx index 51958ad..081eee7 100644 --- a/frontend/src/routes/database/quest-chain/[id].tsx +++ b/frontend/src/routes/database/quest-chain/[id].tsx @@ -21,14 +21,13 @@ export default function QuestChainDetail() { const quest = createMemo(() => { const id = parseInt(params.id as string ?? "", 10); if (isNaN(id)) return undefined; - return questIndex()?.get(id); + return questIndex().get(id); }); const stages = createMemo(() => { const q = quest(); if (!q) return []; const all = BitCraftTables.QuestStageDesc.indexedByMulti("chainDescId"); - if (!all) return []; return all().get(q.id)?.sort((a, b) => q.stages.indexOf(a.id) - q.stages.indexOf(b.id)) ?? []; }); @@ -191,7 +190,7 @@ export default function QuestChainDetail() { ); }]] : undefined} rawData={quest()} - spacetimeTable={BitCraftTables.QuestChainDesc.st_name} + spacetimeTable={BitCraftTables.QuestChainDesc.spacetimeName} objectId={quest()?.id} tabs={[ { diff --git a/frontend/src/routes/database/resource/[id].tsx b/frontend/src/routes/database/resource/[id].tsx index 3f78c69..eb59f53 100644 --- a/frontend/src/routes/database/resource/[id].tsx +++ b/frontend/src/routes/database/resource/[id].tsx @@ -23,7 +23,7 @@ export default function ResourceDetail() { const resource = createMemo(() => { const id = parseInt(params.id as string ?? "", 10); if (isNaN(id)) return undefined; - return resourceIndex()?.get(id); + return resourceIndex().get(id); }); const extractionRecipe = createMemo(() => { @@ -81,7 +81,7 @@ export default function ResourceDetail() { {label: "Compendium Entry", value: !resource()?.compendiumEntry ? false : undefined}, ]} rawData={resource()} - spacetimeTable={BitCraftTables.ResourceDesc.st_name} + spacetimeTable={BitCraftTables.ResourceDesc.spacetimeName} objectId={resource()?.id} chatLink={`(res=${resource()?.id})`} tabs={[ diff --git a/frontend/src/routes/database/skill/[id].tsx b/frontend/src/routes/database/skill/[id].tsx index d3abc95..40c19ea 100644 --- a/frontend/src/routes/database/skill/[id].tsx +++ b/frontend/src/routes/database/skill/[id].tsx @@ -13,7 +13,7 @@ export default function SkillDetail() { const skill = createMemo(() => { const id = parseInt(params.id as string ?? "", 10); if (isNaN(id)) return undefined; - return skillIndex()?.get(id); + return skillIndex().get(id); }); const skillTag = createMemo(() => { const s = skill(); @@ -36,7 +36,7 @@ export default function SkillDetail() { {label: "Max Level", value: skill()?.maxLevel}, ]} rawData={skill()} - spacetimeTable={BitCraftTables.SkillDesc.st_name} + spacetimeTable={BitCraftTables.SkillDesc.spacetimeName} objectId={skill()?.id} chatLink={`(prof=${skill()?.id})`} tabs={[]} diff --git a/frontend/src/routes/database/terraforming/[id].tsx b/frontend/src/routes/database/terraforming/[id].tsx index fe719a6..add74b6 100644 --- a/frontend/src/routes/database/terraforming/[id].tsx +++ b/frontend/src/routes/database/terraforming/[id].tsx @@ -16,7 +16,7 @@ export default function TerraformingDetail() { const recipe = createMemo(() => { const id = parseInt(params.id as string ?? "", 10); if (isNaN(id)) return undefined; - return index()?.get(id); + return index().get(id); }); const tool = createMemo(() => { @@ -41,7 +41,7 @@ export default function TerraformingDetail() { {label: "Tool", value: tool() ? tool()![1] : undefined}, ]} rawData={recipe()} - spacetimeTable={BitCraftTables.TerraformRecipeDesc.st_name} + spacetimeTable={BitCraftTables.TerraformRecipeDesc.spacetimeName} objectId={recipe()?.difference} tabs={[ { diff --git a/frontend/src/routes/database/tool/[id].tsx b/frontend/src/routes/database/tool/[id].tsx index 930385c..0535a2a 100644 --- a/frontend/src/routes/database/tool/[id].tsx +++ b/frontend/src/routes/database/tool/[id].tsx @@ -15,11 +15,11 @@ export default function ToolDetail() { const tool = createMemo(() => { const id = parseInt(params.id as string ?? "", 10); if (isNaN(id)) return undefined; - return toolIndex()?.get(id); + return toolIndex().get(id); }); - const item = createMemo(() => tool() ? itemIndex()?.get(tool()!.itemId) : undefined); - const toolType = createMemo(() => tool() ? toolTypeIndex()?.get(tool()!.toolType) : undefined); + const item = createMemo(() => tool() ? itemIndex().get(tool()!.itemId) : undefined); + const toolType = createMemo(() => tool() ? toolTypeIndex().get(tool()!.toolType) : undefined); return ( : undefined}, ]} rawData={tool()} - spacetimeTable={BitCraftTables.ToolDesc.st_name} + spacetimeTable={BitCraftTables.ToolDesc.spacetimeName} objectId={tool()?.itemId} chatLink={`(item=${item()?.id})`} tabs={[ diff --git a/frontend/src/routes/database/traveler-task/[id].tsx b/frontend/src/routes/database/traveler-task/[id].tsx index 731d463..1ed8cff 100644 --- a/frontend/src/routes/database/traveler-task/[id].tsx +++ b/frontend/src/routes/database/traveler-task/[id].tsx @@ -16,13 +16,13 @@ export default function TravelerTaskDetail() { const task = createMemo(() => { const id = parseInt(params.id as string ?? "", 10); if (isNaN(id)) return undefined; - return index()?.get(id); + return index().get(id); }); const skillName = createMemo(() => { const t = task(); if (!t) return undefined; - return skillIndex()?.get(t.levelRequirement?.skillId)?.name; + return skillIndex().get(t.levelRequirement?.skillId)?.name; }); const xpStr = createMemo(() => { @@ -45,7 +45,7 @@ export default function TravelerTaskDetail() { {label: "XP Reward", value: xpStr()}, ]} rawData={task()} - spacetimeTable={BitCraftTables.TravelerTaskDesc.st_name} + spacetimeTable={BitCraftTables.TravelerTaskDesc.spacetimeName} objectId={task()?.id} tabs={[ { diff --git a/frontend/src/routes/database/traveler-trade/[id].tsx b/frontend/src/routes/database/traveler-trade/[id].tsx index 3e34887..8acf477 100644 --- a/frontend/src/routes/database/traveler-trade/[id].tsx +++ b/frontend/src/routes/database/traveler-trade/[id].tsx @@ -18,7 +18,7 @@ export default function TravelerTradeDetail() { const trade = createMemo(() => { const id = parseInt(params.id as string ?? "", 10); if (isNaN(id)) return undefined; - return index()?.get(id); + return index().get(id); }); const npcDesc = createMemo(() => { @@ -27,7 +27,7 @@ export default function TravelerTradeDetail() { const tagOrdinal = BitCraftTables.TravelerTradeOrderDesc.tagToOrdinal("traveler"); const npcOrdinal = tagOrdinal.get(t.traveler.tag); if (npcOrdinal === undefined) return undefined; - return BitCraftTables.NpcDesc.indexedBy("npcType")()?.get(npcOrdinal); + return BitCraftTables.NpcDesc.indexedBy("npcType")().get(npcOrdinal); }); const npcName = createMemo(() => getTravelerNpcName(trade()?.traveler.tag ?? "")); @@ -76,7 +76,7 @@ export default function TravelerTradeDetail() { ]} summaryContent={() => trade() ? : <>} rawData={trade()} - spacetimeTable={BitCraftTables.TravelerTradeOrderDesc.st_name} + spacetimeTable={BitCraftTables.TravelerTradeOrderDesc.spacetimeName} objectId={trade()?.id} /> ); diff --git a/frontend/src/routes/database/weapon/[id].tsx b/frontend/src/routes/database/weapon/[id].tsx index a76eba4..6a018f6 100644 --- a/frontend/src/routes/database/weapon/[id].tsx +++ b/frontend/src/routes/database/weapon/[id].tsx @@ -18,11 +18,11 @@ export default function WeaponDetail() { const weapon = createMemo(() => { const id = parseInt(params.id as string ?? "", 10); if (isNaN(id)) return undefined; - return index()?.get(id); + return index().get(id); }); - const item = createMemo(() => weapon() ? itemIndex()?.get(weapon()!.itemId) : undefined); - const weaponType = createMemo(() => weapon() ? weaponTypeIndex()?.get(weapon()!.weaponType) : undefined); + const item = createMemo(() => weapon() ? itemIndex().get(weapon()!.itemId) : undefined); + const weaponType = createMemo(() => weapon() ? weaponTypeIndex().get(weapon()!.weaponType) : undefined); const combatActions = createMemo(() => { const w = weapon(); @@ -52,7 +52,7 @@ export default function WeaponDetail() { {label: "Hunting", value: weaponType()?.hunting}, ]} rawData={weapon()} - spacetimeTable={BitCraftTables.WeaponDesc.st_name} + spacetimeTable={BitCraftTables.WeaponDesc.spacetimeName} objectId={weapon()?.itemId} chatLink={`(item=${item()?.id})`} tabs={[ diff --git a/frontend/src/routes/tools/placeable-graph.tsx b/frontend/src/routes/tools/placeable-graph.tsx index d52bf05..60d475e 100644 --- a/frontend/src/routes/tools/placeable-graph.tsx +++ b/frontend/src/routes/tools/placeable-graph.tsx @@ -66,7 +66,7 @@ export default function PlaceableGraphTool() { } function placementSelectionRow(placement: PlaceablePlacementDesc) { - const placeable = BitCraftTables.PlaceableDesc.indexedBy("id")()?.get(placement.placedPlaceableId); + const placeable = BitCraftTables.PlaceableDesc.indexedBy("id")().get(placement.placedPlaceableId); const placeableName = placeable?.name; return ( <> From 2244da7164085977de3053c11de16906087805f5 Mon Sep 17 00:00:00 2001 From: wizjany Date: Wed, 15 Jul 2026 23:43:17 -0400 Subject: [PATCH 7/8] Version checker should link to current page on preview. --- .../components/data-table/table-faceted-filter.tsx | 12 +++++++----- frontend/src/components/version-checker.tsx | 8 +++++--- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/frontend/src/components/data-table/table-faceted-filter.tsx b/frontend/src/components/data-table/table-faceted-filter.tsx index 9ae3299..8f2644a 100644 --- a/frontend/src/components/data-table/table-faceted-filter.tsx +++ b/frontend/src/components/data-table/table-faceted-filter.tsx @@ -441,11 +441,13 @@ export function TableFacetedFilter(props: TableFacetedFilterProps) } return ( - { - setPopoverOpen(open); - setEditingWithNumberInputs(false); - }} + { + setPopoverOpen(open); + setEditingWithNumberInputs(false); + }} > } variant="outline" size="sm" onclick={() => setPopoverOpen(!popoverOpen())} diff --git a/frontend/src/components/version-checker.tsx b/frontend/src/components/version-checker.tsx index d9497e0..f5bee77 100644 --- a/frontend/src/components/version-checker.tsx +++ b/frontend/src/components/version-checker.tsx @@ -1,3 +1,4 @@ +import {A, useLocation} from "@solidjs/router"; import {TbOutlineExternalLink as IconExternal, TbOutlineInfoCircle as IconCurrent, TbOutlineInfoTriangle as IconOutdated} from "solid-icons/tb"; import {createResource, onCleanup, onMount, Show} from "solid-js"; import {Button} from "~/components/ui/button"; @@ -36,6 +37,7 @@ function hasDescription(version: VersionInfo): boolean { export function VersionChecker() { const [latestVersion, {refetch}] = createResource(fetchLatestVersion); + const location = useLocation(); onMount(() => { let timer = setInterval(refetch, 15 * 60 * 1000); // 15 minutes @@ -53,7 +55,7 @@ export function VersionChecker() { return (
    - +