From 55fe198f78445fb9e51b6da9a05d7409b9ef1f27 Mon Sep 17 00:00:00 2001 From: Alex Mooney Date: Mon, 26 Jan 2026 11:28:35 -0800 Subject: [PATCH 1/6] List all crafting recipes by level on skill page --- src/types/Skill.svelte | 45 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/types/Skill.svelte b/src/types/Skill.svelte index b3b31209..4389c55b 100644 --- a/src/types/Skill.svelte +++ b/src/types/Skill.svelte @@ -48,6 +48,31 @@ const itemsUsingSkill = data ) as SupportedTypesWithMapped["GUN"][]; itemsUsingSkill.sort(byName); +const craftingRecipes = data + .byType("recipe") + .filter( + (r) => + r.skill_used === item.id && r.result && data.byIdMaybe("item", r.result) + ); + +const recipesByLevel = new Map(); +for (const recipe of craftingRecipes) { + const level = recipe.difficulty ?? 0; + if (!recipesByLevel.has(level)) recipesByLevel.set(level, []); + recipesByLevel.get(level)!.push(recipe); +} +const recipesByLevelList = [...recipesByLevel.entries()].sort( + (a, b) => a[0] - b[0] +); +recipesByLevelList.forEach(([, recipes]) => { + recipes.sort((a, b) => { + const itemA = data.byId("item", a.result!); + const itemB = data.byId("item", b.result!); + if (!itemA || !itemB) return 0; // If either item doesn't exist, consider them equal + return singularName(itemA).localeCompare(singularName(itemB)); + }); +}); + const practiceRecipes = data .byType("practice") .filter((r) => r.skill_used === item.id); @@ -91,6 +116,26 @@ practiceRecipes.sort( {/if} +{#if craftingRecipes.length} +
+

{t("Crafting Recipes", { _context: "Skill" })}

+
+ {#each recipesByLevelList as [level, recipes]} +
Level {level}
+
+
    + {#each recipes as recipe} + {#if recipe.result} +
  • + {/if} + {/each} +
+
+ {/each} +
+
+{/if} + {#if practiceRecipes.length}

{t("Practice Recipes", { _context: "Skill" })}

{#each practiceRecipes as recipe} From 5c4fdede57fdcbbf4eaf93461324ed08eda63c6f Mon Sep 17 00:00:00 2001 From: Alex Mooney Date: Mon, 26 Jan 2026 11:33:36 -0800 Subject: [PATCH 2/6] Collapse crafting recipes if there are too many --- src/types/Skill.svelte | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/types/Skill.svelte b/src/types/Skill.svelte index 4389c55b..0f6c878f 100644 --- a/src/types/Skill.svelte +++ b/src/types/Skill.svelte @@ -123,13 +123,16 @@ practiceRecipes.sort( {#each recipesByLevelList as [level, recipes]}
Level {level}
-
    - {#each recipes as recipe} - {#if recipe.result} -
  • - {/if} - {/each} -
+
+ {recipes.length} recipes +
    + {#each recipes as recipe} + {#if recipe.result} +
  • + {/if} + {/each} +
+
{/each} From 6a20d8860403d0a5924bb4c4e4918264e4a45d39 Mon Sep 17 00:00:00 2001 From: Alex Mooney Date: Mon, 26 Jan 2026 11:50:19 -0800 Subject: [PATCH 3/6] Show summary of how to get craft recipe too --- src/types/Skill.svelte | 74 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/src/types/Skill.svelte b/src/types/Skill.svelte index 0f6c878f..1fd88b9d 100644 --- a/src/types/Skill.svelte +++ b/src/types/Skill.svelte @@ -73,6 +73,74 @@ recipesByLevelList.forEach(([, recipes]) => { }); }); +function getRecipeLearningInfo(recipe: any, recipeLevel: number): string { + const parts = []; + + // Check for autolearn + if (recipe.autolearn) { + if (Array.isArray(recipe.autolearn)) { + // Multiple skills for autolearn - check if it's just current skill at current level + if ( + recipe.autolearn.length === 1 && + recipe.autolearn[0][0] === item.id && + recipe.autolearn[0][1] === recipeLevel + ) { + parts.push("Autolearn"); + } else { + const autolearns = recipe.autolearn.map( + ([skill, level]: [string, number]) => { + const skillData = data.byIdMaybe("skill", skill); + const skillName = skillData ? singularName(skillData) : skill; + return `${skillName} ${level}`; + } + ); + parts.push(`Autolearn ${autolearns.join(", ")}`); + } + } else { + // Single skill autolearn based on recipe difficulty + if (recipe.skill_used) { + const recipeDifficulty = recipe.difficulty ?? 0; + // If autolearn skill matches current skill and level matches the group level, just say "Autolearn" + if (recipe.skill_used === item.id && recipeDifficulty === recipeLevel) { + parts.push("Autolearn"); + } else { + const skillData = data.byIdMaybe("skill", recipe.skill_used); + const skillName = skillData + ? singularName(skillData) + : recipe.skill_used; + parts.push(`Autolearn ${skillName} ${recipeDifficulty}`); + } + } + } + } + + // Check for book learning + const writtenIn = Array.isArray(recipe.book_learn) + ? [...recipe.book_learn] + : [...Object.entries((recipe.book_learn ?? {}) as Record)].map( + ([k, v]) => [k, v.skill_level ?? v] + ); + + if (writtenIn.length > 0) { + if (writtenIn.length >= 3) { + parts.push(`Written in ${writtenIn.length} books`); + } else { + const bookNames = writtenIn.map(([bookId, level]) => { + const book = data.byIdMaybe("item", bookId); + const bookName = book ? singularName(book) : bookId; + return level ? `${bookName} (${level})` : bookName; + }); + if (writtenIn.length === 2) { + parts.push(`Written in ${bookNames.join(" and ")}`); + } else { + parts.push(`Written in ${bookNames[0]}`); + } + } + } + + return parts.length > 0 ? ` (${parts.join("; ")})` : ""; +} + const practiceRecipes = data .byType("practice") .filter((r) => r.skill_used === item.id); @@ -128,7 +196,11 @@ practiceRecipes.sort(
    {#each recipes as recipe} {#if recipe.result} -
  • +
  • + {getRecipeLearningInfo(recipe, level)} +
  • {/if} {/each}
From 3c4d4ba2aac568b8231c9f22343bd85425d9b40f Mon Sep 17 00:00:00 2001 From: Alex Mooney Date: Mon, 26 Jan 2026 11:59:12 -0800 Subject: [PATCH 4/6] Exclude never_learn recipes from the lists --- src/types/Skill.svelte | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/types/Skill.svelte b/src/types/Skill.svelte index 1fd88b9d..756830b7 100644 --- a/src/types/Skill.svelte +++ b/src/types/Skill.svelte @@ -52,7 +52,10 @@ const craftingRecipes = data .byType("recipe") .filter( (r) => - r.skill_used === item.id && r.result && data.byIdMaybe("item", r.result) + r.skill_used === item.id && + r.result && + data.byIdMaybe("item", r.result) && + !r.never_learn ); const recipesByLevel = new Map(); From bdeb61480e2f8016941b57479f1dcb881f55c76d Mon Sep 17 00:00:00 2001 From: Alex Mooney Date: Mon, 26 Jan 2026 11:59:37 -0800 Subject: [PATCH 5/6] Add skills to front page --- src/App.svelte | 1 + 1 file changed, 1 insertion(+) diff --git a/src/App.svelte b/src/App.svelte index 4059189c..0cd62ff9 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -503,6 +503,7 @@ Anyway?`, {t("Achievements")} / {t("Conducts")} +
  • {t("Skills")}
  • {t("Proficiencies")}
  • From 4b84b6e61a26251d635130f6e8cb70c6f7d8a253 Mon Sep 17 00:00:00 2001 From: Alex Mooney Date: Thu, 19 Feb 2026 14:11:51 -0800 Subject: [PATCH 6/6] Change details into LimitedList --- src/types/Skill.svelte | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/types/Skill.svelte b/src/types/Skill.svelte index 756830b7..b7bfb819 100644 --- a/src/types/Skill.svelte +++ b/src/types/Skill.svelte @@ -194,20 +194,13 @@ practiceRecipes.sort( {#each recipesByLevelList as [level, recipes]}
    Level {level}
    -
    - {recipes.length} recipes -
      - {#each recipes as recipe} - {#if recipe.result} -
    • - {getRecipeLearningInfo(recipe, level)} -
    • - {/if} - {/each} -
    -
    + + {#if item.result} + {getRecipeLearningInfo(item, level)} + {/if} +
    {/each}