a collection of tools for fly for fun universe skillulator.lol

clean up treeStore

besaid.zone 0bd99d32 c3b8abec

verified
Changed files
+344 -320
apps
flyff-exporter
src
scripts
skillulator
src
+6 -5
apps/flyff-exporter/src/scripts/skill-tree.ts
··· 22 22 // remove vagrant since we don't care about that 23 23 .filter((job) => job.id !== 9686); 24 24 25 - const skillsWithJobId = skills.map((skill) => ({ 25 + const skillTreeWithoutJobs = skills.map((skill) => ({ 26 26 id: skill.id, 27 27 name: skill.name, 28 28 icon: skill.icon, ··· 40 40 41 41 // here we should build the tree with just second and third jobs BUT 42 42 // the skills array should contain skills from the 1st, 2nd and 3rd job 43 - const tree = refinedJobs 43 + const finalTree = refinedJobs 44 44 .map((job) => ({ 45 45 ...job, 46 46 skills: [ 47 47 // we also get the skills for the parent, which should be the 1st job 48 - ...skillsWithJobId.filter((skill) => skill.job === job.parent), 48 + ...skillTreeWithoutJobs.filter((skill) => skill.job === job.parent), 49 49 // filter for the skills that belong to the job 50 - ...skillsWithJobId.filter((skill) => skill.job === job.id), 50 + ...skillTreeWithoutJobs.filter((skill) => skill.job === job.id), 51 51 ], 52 52 })) 53 + // remove the 1st jobs after combining skills 53 54 .filter((job) => job.parent !== 9686); 54 55 55 56 await Bun.write( 56 57 "./src/exports/skill-tree.json", 57 - JSON.stringify(tree, null, 2), 58 + JSON.stringify(finalTree, null, 2), 58 59 ); 59 60 } 60 61
+9 -9
apps/skillulator/src/main.tsx
··· 9 9 const router = createRouter({ routeTree }); 10 10 11 11 declare module "@tanstack/react-router" { 12 - interface Register { 13 - router: typeof router; 14 - } 12 + interface Register { 13 + router: typeof router; 14 + } 15 15 } 16 16 17 17 const rootElement = document.getElementById("root")!; 18 18 if (!rootElement.innerHTML) { 19 - const root = ReactDOM.createRoot(rootElement); 20 - root.render( 21 - <React.StrictMode> 22 - <RouterProvider router={router} /> 23 - </React.StrictMode>, 24 - ); 19 + const root = ReactDOM.createRoot(rootElement); 20 + root.render( 21 + <React.StrictMode> 22 + <RouterProvider router={router} /> 23 + </React.StrictMode>, 24 + ); 25 25 }
+4 -4
apps/skillulator/src/routes/c.$class.tsx
··· 24 24 const createPreloadedSkillTree = useTreeStore( 25 25 (state) => state.createPreloadedSkillTree, 26 26 ); 27 - const setSkillPoints = useTreeStore((state) => state.setSkillPoints); 27 + const initSkillPoints = useTreeStore((state) => state.initSkillPoints); 28 28 const skillPoints = useTreeStore((state) => state.skillPoints); 29 29 const resetSkillTree = useTreeStore((state) => state.resetSkillTree); 30 30 ··· 42 42 43 43 const handleLevelChange = useCallback( 44 44 (event: ChangeEvent<HTMLInputElement>) => { 45 - setSkillPoints(jobId!, +event.target.value); 45 + initSkillPoints(jobId!, +event.target.value); 46 46 setLevel(+event.target.value); 47 47 }, 48 48 [], ··· 71 71 useEffect(() => { 72 72 const code = new URLSearchParams(window.location.search).get("tree") ?? ""; 73 73 if (!code) { 74 - setSkillPoints(jobId!, level); 74 + initSkillPoints(jobId!, level); 75 75 return; 76 76 } 77 77 ··· 86 86 87 87 createPreloadedSkillTree(jobId!, untangledSkillMap); 88 88 89 - setSkillPoints(jobId!, +characterLevel!); 89 + initSkillPoints(jobId!, +characterLevel!); 90 90 }, []); 91 91 92 92 const { i18n } = useTranslation();
+208 -208
apps/skillulator/src/utils/index.ts
··· 1 - import { State } from "../zustand/treeStore"; 1 + import type { State } from "../zustand/treeStore"; 2 2 3 3 export function getJobById(jobId: number, jobs: State["jobTree"]) { 4 - return jobs.find((job) => job.id === jobId); 4 + return jobs.filter((job) => job.id === jobId).at(0); 5 5 } 6 6 7 7 export function getSkillById( 8 - skillId: number, 9 - skills: State["jobTree"][number]["skills"], 8 + skillId: number, 9 + skills: State["jobTree"][number]["skills"], 10 10 ) { 11 - return skills.find((skill) => skill.id === skillId); 11 + return skills.find((skill) => skill.id === skillId); 12 12 } 13 13 14 14 export function getJobByName(jobName: string, jobs: State["jobTree"]) { 15 - return jobs.find( 16 - (job) => job.name.en.toLowerCase() === jobName.toLowerCase(), 17 - ); 15 + return jobs.find( 16 + (job) => job.name.en.toLowerCase() === jobName.toLowerCase(), 17 + ); 18 18 } 19 19 20 20 export function encodeTree( 21 - skills: State["jobTree"][number]["skills"], 22 - characterLevel: number, 21 + skills: State["jobTree"][number]["skills"], 22 + characterLevel: number, 23 23 ) { 24 - return ( 25 - skills?.map((skill) => `${skill.id}:${skill.skillLevel}`).join(",") + 26 - `#${characterLevel}` 27 - ); 24 + return ( 25 + skills?.map((skill) => `${skill.id}:${skill.skillLevel}`).join(",") + 26 + `#${characterLevel}` 27 + ); 28 28 } 29 29 30 30 export function decodeTree(encodedSkills: string) { 31 - const characterLevel = encodedSkills.split("#").at(1); 32 - const decodedTree = encodedSkills.split("#").at(0); 33 - return { 34 - untangledSkillMap: decodedTree! 35 - .split(",") 36 - .map((skill) => skill.split(":")) 37 - .map((s) => ({ skill: +s[0], level: +s[1] })), 38 - characterLevel, 39 - }; 31 + const characterLevel = encodedSkills.split("#").at(1); 32 + const decodedTree = encodedSkills.split("#").at(0); 33 + return { 34 + untangledSkillMap: decodedTree! 35 + .split(",") 36 + .map((skill) => skill.split(":")) 37 + .map((s) => ({ skill: +s[0], level: +s[1] })), 38 + characterLevel, 39 + }; 40 40 } 41 41 42 42 export function getSkillPointsForLevel(characterLevel: number) { 43 - switch (true) { 44 - case characterLevel >= 15 && characterLevel <= 20: 45 - return characterLevel * 2; 46 - case characterLevel > 20 && characterLevel <= 40: 47 - return (characterLevel - 20) * 3 + 20 * 2; 48 - case characterLevel > 40 && characterLevel <= 60: 49 - return (characterLevel - 40) * 4 + 20 * 3 + 20 * 2; 50 - case characterLevel > 60 && characterLevel <= 80: 51 - return (characterLevel - 60) * 5 + 20 * 4 + 20 * 3 + 20 * 2; 52 - case characterLevel > 80 && characterLevel <= 100: 53 - return (characterLevel - 80) * 6 + 20 * 5 + 20 * 4 + 20 * 3 + 20 * 2; 54 - case characterLevel > 100 && characterLevel <= 120: 55 - return ( 56 - (characterLevel - 100) * 7 + 20 * 6 + 20 * 5 + 20 * 4 + 20 * 3 + 20 * 2 57 - ); 58 - case characterLevel > 120 && characterLevel <= 140: 59 - return ( 60 - (characterLevel - 120) * 8 + 61 - 20 * 7 + 62 - 20 * 6 + 63 - 20 * 5 + 64 - 20 * 4 + 65 - 20 * 3 + 66 - 20 * 2 67 - ); 68 - case characterLevel > 140 && characterLevel <= 150: 69 - return ( 70 - (characterLevel - 140) * 1 + 71 - 20 * 8 + 72 - 20 * 7 + 73 - 20 * 6 + 74 - 20 * 5 + 75 - 20 * 4 + 76 - 20 * 3 + 77 - 20 * 2 78 - ); 79 - case characterLevel > 150 && characterLevel <= 160: 80 - return ( 81 - (characterLevel - 150) * 2 + 82 - 20 * 1 + 83 - 20 * 8 + 84 - 20 * 7 + 85 - 20 * 6 + 86 - 20 * 5 + 87 - 20 * 4 + 88 - 20 * 3 + 89 - 20 * 2 90 - ); 91 - case characterLevel > 160 && characterLevel <= 165: 92 - return ( 93 - (characterLevel - 160) * 2 + 94 - 20 * 1 + 95 - 20 * 8 + 96 - 20 * 7 + 97 - 20 * 6 + 98 - 20 * 5 + 99 - 20 * 4 + 100 - 20 * 3 + 101 - 20 * 2 + 102 - 20 * 2 103 - ); 104 - default: 105 - return 0; 106 - } 43 + switch (true) { 44 + case characterLevel >= 15 && characterLevel <= 20: 45 + return characterLevel * 2; 46 + case characterLevel > 20 && characterLevel <= 40: 47 + return (characterLevel - 20) * 3 + 20 * 2; 48 + case characterLevel > 40 && characterLevel <= 60: 49 + return (characterLevel - 40) * 4 + 20 * 3 + 20 * 2; 50 + case characterLevel > 60 && characterLevel <= 80: 51 + return (characterLevel - 60) * 5 + 20 * 4 + 20 * 3 + 20 * 2; 52 + case characterLevel > 80 && characterLevel <= 100: 53 + return (characterLevel - 80) * 6 + 20 * 5 + 20 * 4 + 20 * 3 + 20 * 2; 54 + case characterLevel > 100 && characterLevel <= 120: 55 + return ( 56 + (characterLevel - 100) * 7 + 20 * 6 + 20 * 5 + 20 * 4 + 20 * 3 + 20 * 2 57 + ); 58 + case characterLevel > 120 && characterLevel <= 140: 59 + return ( 60 + (characterLevel - 120) * 8 + 61 + 20 * 7 + 62 + 20 * 6 + 63 + 20 * 5 + 64 + 20 * 4 + 65 + 20 * 3 + 66 + 20 * 2 67 + ); 68 + case characterLevel > 140 && characterLevel <= 150: 69 + return ( 70 + (characterLevel - 140) * 1 + 71 + 20 * 8 + 72 + 20 * 7 + 73 + 20 * 6 + 74 + 20 * 5 + 75 + 20 * 4 + 76 + 20 * 3 + 77 + 20 * 2 78 + ); 79 + case characterLevel > 150 && characterLevel <= 160: 80 + return ( 81 + (characterLevel - 150) * 2 + 82 + 20 * 1 + 83 + 20 * 8 + 84 + 20 * 7 + 85 + 20 * 6 + 86 + 20 * 5 + 87 + 20 * 4 + 88 + 20 * 3 + 89 + 20 * 2 90 + ); 91 + case characterLevel > 160 && characterLevel <= 165: 92 + return ( 93 + (characterLevel - 160) * 2 + 94 + 20 * 1 + 95 + 20 * 8 + 96 + 20 * 7 + 97 + 20 * 6 + 98 + 20 * 5 + 99 + 20 * 4 + 100 + 20 * 3 + 101 + 20 * 2 + 102 + 20 * 2 103 + ); 104 + default: 105 + return 0; 106 + } 107 107 } 108 108 109 109 // eh this could be named better lol 110 110 export const classSkillPoints = { 111 - // elementor 112 - 9150: { 113 - firstJobSP: 90, 114 - secondJobSP: 300, 115 - }, 116 - //psykeeper 117 - 5709: { 118 - firstJobSP: 90, 119 - secondJobSP: 90, 120 - }, 121 - // blade 122 - 2246: { 123 - firstJobSP: 60, 124 - secondJobSP: 80, 125 - }, 126 - // knight 127 - 5330: { 128 - firstJobSP: 60, 129 - secondJobSP: 80, 130 - }, 131 - // billposter 132 - 7424: { 133 - firstJobSP: 60, 134 - secondJobSP: 120, 135 - }, 136 - // ringmaster 137 - 9389: { 138 - firstJobSP: 60, 139 - secondJobSP: 100, 140 - }, 141 - // ranger 142 - 9295: { 143 - firstJobSP: 50, 144 - secondJobSP: 100, 145 - }, 146 - // jester 147 - 3545: { 148 - firstJobSP: 50, 149 - secondJobSP: 100, 150 - }, 111 + // elementor 112 + 9150: { 113 + firstJobSP: 90, 114 + secondJobSP: 300, 115 + }, 116 + //psykeeper 117 + 5709: { 118 + firstJobSP: 90, 119 + secondJobSP: 90, 120 + }, 121 + // blade 122 + 2246: { 123 + firstJobSP: 60, 124 + secondJobSP: 80, 125 + }, 126 + // knight 127 + 5330: { 128 + firstJobSP: 60, 129 + secondJobSP: 80, 130 + }, 131 + // billposter 132 + 7424: { 133 + firstJobSP: 60, 134 + secondJobSP: 120, 135 + }, 136 + // ringmaster 137 + 9389: { 138 + firstJobSP: 60, 139 + secondJobSP: 100, 140 + }, 141 + // ranger 142 + 9295: { 143 + firstJobSP: 50, 144 + secondJobSP: 100, 145 + }, 146 + // jester 147 + 3545: { 148 + firstJobSP: 50, 149 + secondJobSP: 100, 150 + }, 151 151 }; 152 152 153 153 export function getJobTotalSkillPoints( 154 - jobMap: typeof classSkillPoints, 155 - jobId: number, 156 - characterLevel: number, 154 + jobMap: typeof classSkillPoints, 155 + jobId: number, 156 + characterLevel: number, 157 157 ) { 158 - if (characterLevel >= 60) { 159 - return ( 160 - getSkillPointsForLevel(characterLevel) + 161 - jobMap[jobId].firstJobSP + 162 - jobMap[jobId].secondJobSP 163 - ); 164 - } 158 + if (characterLevel >= 60) { 159 + return ( 160 + getSkillPointsForLevel(characterLevel) + 161 + jobMap[jobId].firstJobSP + 162 + jobMap[jobId].secondJobSP 163 + ); 164 + } 165 165 166 - return getSkillPointsForLevel(characterLevel) + jobMap[jobId].firstJobSP; 166 + return getSkillPointsForLevel(characterLevel) + jobMap[jobId].firstJobSP; 167 167 } 168 168 169 169 export const languages = [ 170 - { 171 - label: "en", 172 - value: "en", 173 - language: "English", 174 - }, 175 - { 176 - label: "pt-BR", 177 - value: "br", 178 - locale: "pt-BR", 179 - language: "Português", 180 - }, 181 - { 182 - label: "zh", 183 - value: "cns", 184 - locale: "zh-CN", 185 - language: "Chinese", 186 - }, 187 - { 188 - label: "ja", 189 - value: "jp", 190 - language: "Japanese", 191 - }, 192 - { 193 - label: "ko", 194 - value: "kr", 195 - language: "Korean", 196 - }, 197 - { 198 - label: "es", 199 - value: "sp", 200 - language: "Spanish", 201 - }, 202 - { 203 - label: "ru", 204 - value: "ru", 205 - language: "Russian", 206 - }, 207 - { 208 - label: "de", 209 - value: "de", 210 - language: "German", 211 - }, 212 - { 213 - label: "fi", 214 - value: "fi", 215 - language: "Finnish", 216 - }, 217 - { 218 - label: "id", 219 - value: "id", 220 - language: "Indonesian", 221 - }, 222 - { 223 - label: "it", 224 - value: "it", 225 - language: "Italian", 226 - }, 227 - { 228 - label: "nl", 229 - value: "nl", 230 - language: "Dutch", 231 - }, 232 - { 233 - label: "pl", 234 - value: "pl", 235 - language: "Polish", 236 - }, 170 + { 171 + label: "en", 172 + value: "en", 173 + language: "English", 174 + }, 175 + { 176 + label: "pt-BR", 177 + value: "br", 178 + locale: "pt-BR", 179 + language: "Português", 180 + }, 181 + { 182 + label: "zh", 183 + value: "cns", 184 + locale: "zh-CN", 185 + language: "Chinese", 186 + }, 187 + { 188 + label: "ja", 189 + value: "jp", 190 + language: "Japanese", 191 + }, 192 + { 193 + label: "ko", 194 + value: "kr", 195 + language: "Korean", 196 + }, 197 + { 198 + label: "es", 199 + value: "sp", 200 + language: "Spanish", 201 + }, 202 + { 203 + label: "ru", 204 + value: "ru", 205 + language: "Russian", 206 + }, 207 + { 208 + label: "de", 209 + value: "de", 210 + language: "German", 211 + }, 212 + { 213 + label: "fi", 214 + value: "fi", 215 + language: "Finnish", 216 + }, 217 + { 218 + label: "id", 219 + value: "id", 220 + language: "Indonesian", 221 + }, 222 + { 223 + label: "it", 224 + value: "it", 225 + language: "Italian", 226 + }, 227 + { 228 + label: "nl", 229 + value: "nl", 230 + language: "Dutch", 231 + }, 232 + { 233 + label: "pl", 234 + value: "pl", 235 + language: "Polish", 236 + }, 237 237 ]; 238 238 239 239 export function getLanguageForSkill( 240 - langs: typeof languages, 241 - appLanguage: string, 240 + langs: typeof languages, 241 + appLanguage: string, 242 242 ) { 243 - return langs.find((lang) => lang.label === appLanguage)?.value; 243 + return langs.find((lang) => lang.label === appLanguage)?.value; 244 244 }
+117 -94
apps/skillulator/src/zustand/treeStore.ts
··· 18 18 increaseSkillPoint: (jobId: number, skillId: number) => void; 19 19 increaseSkillToMax: (jobId: number, skillId: number) => void; 20 20 decreaseSkillPoint: (jobId: number, skillId: number) => void; 21 - setSkillPoints: (jobId: number, characterLevel: number) => void; 21 + initSkillPoints: (jobId: number, characterLevel: number) => void; 22 22 23 23 createPreloadedSkillTree: ( 24 24 jobId: number, 25 - skills: Array<Record<string, unknown>>, 25 + skills: Array<Record<string, number>>, 26 26 ) => void; 27 27 resetSkillTree: (jobId: number) => void; 28 28 }; ··· 33 33 skillPoints: 0, 34 34 }; 35 35 36 - export const useTreeStore = create<State & Actions>()((set, get) => ({ 36 + export const useTreeStore = create<State & Actions>()((set, _) => ({ 37 37 jobTree, 38 38 classSkillPoints, 39 39 skillPoints: 0, 40 - setSkillPoints: (jobId: number, characterLevel: number) => 40 + initSkillPoints: (jobId: number, characterLevel: number) => 41 41 set( 42 42 produce((state: State) => { 43 43 // need to figure out how many skill points are already spent and subtract it 44 - let skillPoints = getJobTotalSkillPoints( 44 + const skillPoints = getJobTotalSkillPoints( 45 45 state.classSkillPoints, 46 46 jobId, 47 47 characterLevel, ··· 49 49 50 50 let skillPointsToSubtract = 0; 51 51 const job = getJobById(jobId, state.jobTree); 52 - console.log(get(job)); 53 - job?.skills.forEach((s) => { 54 - skillPointsToSubtract += s.skillLevel * s.points; 55 - }); 56 - let remainingSkillPoints = skillPoints - skillPointsToSubtract; 52 + 53 + if (job) { 54 + for (const skill of job.skills) { 55 + skillPointsToSubtract += skill.skillLevel * skill.points; 56 + } 57 + } 58 + 59 + const remainingSkillPoints = skillPoints - skillPointsToSubtract; 57 60 58 61 state.skillPoints = remainingSkillPoints; 59 62 return state; ··· 62 65 increaseSkillPoint: (jobId: number, skillId: number) => 63 66 set( 64 67 produce((state: State) => { 65 - // find the job 66 68 const job = getJobById(jobId, state.jobTree); 67 - // find skill 68 - const skill = getSkillById(skillId, job?.skills!); 69 - if (skill!.skillLevel === skill!.levels) return state; 70 - if (skill!.points > state.skillPoints) return state; 71 - skill!.skillLevel += 1; 72 - state.skillPoints -= skill!.points; 69 + if (job) { 70 + const skill = getSkillById(skillId, job?.skills); 71 + if (skill) { 72 + if (skill.skillLevel === skill.levels) return state; 73 + if (skill.points > state.skillPoints) return state; 74 + skill.skillLevel += 1; 75 + state.skillPoints -= skill.points; 76 + } 73 77 74 - // find all required skills 75 - // if it's the min level, switch hasMinLevel to true 76 - job?.skills.forEach((s) => { 77 - const foundSkill = s.requirements.find((sz) => sz.skill === skillId); 78 - const skillIndex = s.requirements.findIndex( 79 - (sx) => sx.skill === skillId, 80 - ); 81 - if ( 82 - typeof foundSkill !== "undefined" && 83 - foundSkill.level === skill!.skillLevel 84 - ) { 85 - s.requirements[skillIndex].hasMinLevel = true; 78 + // find all required skills 79 + // if it's the min level, switch hasMinLevel to true 80 + for (const skill of job.skills) { 81 + const requiredSkill = skill.requirements.find( 82 + (required) => required.skill === skillId, 83 + ); 84 + const requiredSkillIndex = skill.requirements.findIndex( 85 + (required) => required.skill === skillId, 86 + ); 87 + if ( 88 + typeof requiredSkill !== "undefined" && 89 + requiredSkill.level === skill.skillLevel 90 + ) { 91 + skill.requirements[requiredSkillIndex].hasMinLevel = true; 92 + } 86 93 } 87 - }); 94 + } 95 + 88 96 return state; 89 97 }), 90 98 ), 91 99 decreaseSkillPoint: (jobId: number, skillId: number) => 92 100 set( 93 101 produce((state: State) => { 94 - // find the job 95 102 const job = getJobById(jobId, state.jobTree); 96 - // find skill 97 - const skill = getSkillById(skillId, job?.skills!); 98 - if (skill?.skillLevel === 0) return state; 99 - skill!.skillLevel -= 1; 100 - state.skillPoints += skill!.points; 101 - // find all required skills 102 - // if the skillLevel is less than the required skills required level switch to false 103 - job?.skills.forEach((s) => { 104 - const foundSkill = s.requirements.find((sz) => sz.skill === skillId); 105 - const skillIndex = s.requirements.findIndex( 106 - (sx) => sx.skill === skillId, 107 - ); 108 - if ( 109 - typeof foundSkill !== "undefined" && 110 - skill!.skillLevel < foundSkill.level 111 - ) { 112 - s.requirements[skillIndex].hasMinLevel = false; 103 + if (job) { 104 + const skill = getSkillById(skillId, job.skills); 105 + if (skill) { 106 + if (skill.skillLevel === 0) return state; 107 + skill.skillLevel -= 1; 108 + state.skillPoints += skill.points; 109 + } 110 + 111 + // find all required skills 112 + // if the skillLevel is less than the required skills required level switch to false 113 + for (const skill of job.skills) { 114 + const requiredSkill = skill.requirements.find( 115 + (required) => required.skill === skillId, 116 + ); 117 + const requiredSkillIndex = skill.requirements.findIndex( 118 + (required) => required.skill === skillId, 119 + ); 120 + if ( 121 + typeof requiredSkill !== "undefined" && 122 + skill.skillLevel < requiredSkill.level 123 + ) { 124 + skill.requirements[requiredSkillIndex].hasMinLevel = false; 125 + } 113 126 } 114 - }); 127 + } 115 128 }), 116 129 ), 117 130 createPreloadedSkillTree: ( 118 131 jobId: number, 119 - predefinedSkills: Array<Record<string, unknown>>, 132 + predefinedSkills: Array<Record<string, number>>, 120 133 ) => 121 134 set( 122 135 produce((state: State) => { ··· 124 137 // we check the skill id and level and update the skillLevel accordingly 125 138 // we also need to do the check to see if the skill is the min level 126 139 const job = getJobById(jobId, state.jobTree); 127 - job?.skills.forEach((originalTreeSkill) => { 128 - predefinedSkills.forEach((predefinedTreeSkill) => { 129 - if (originalTreeSkill.id === predefinedTreeSkill.skill) { 130 - originalTreeSkill.skillLevel = predefinedTreeSkill.level; 140 + if (job) { 141 + for (const originalTreeSkill of job.skills) { 142 + for (const preloadedSkill of predefinedSkills) { 143 + if (originalTreeSkill.id === preloadedSkill.skill) { 144 + originalTreeSkill.skillLevel = preloadedSkill.level; 145 + } 131 146 } 132 - }); 147 + const skill = getSkillById(originalTreeSkill.id, job.skills); 133 148 134 - const skill = getSkillById(originalTreeSkill.id, job?.skills!); 135 - 136 - job?.skills.forEach((s) => { 137 - const foundSkill = s.requirements.find( 138 - (sz) => sz.skill === originalTreeSkill.id, 139 - ); 140 - const skillIndex = s.requirements.findIndex( 141 - (sx) => sx.skill === originalTreeSkill.id, 142 - ); 143 - if ( 144 - typeof foundSkill !== "undefined" && 145 - foundSkill.level <= skill!.skillLevel 146 - ) { 147 - s.requirements[skillIndex].hasMinLevel = true; 149 + if (skill) { 150 + for (const s of job.skills) { 151 + const requiredSkill = s.requirements.find( 152 + (required) => required.skill === originalTreeSkill.id, 153 + ); 154 + const requiredSkillIndex = s.requirements.findIndex( 155 + (required) => required.skill === originalTreeSkill.id, 156 + ); 157 + if ( 158 + typeof requiredSkill !== "undefined" && 159 + requiredSkill.level <= skill.skillLevel 160 + ) { 161 + s.requirements[requiredSkillIndex].hasMinLevel = true; 162 + } 163 + } 148 164 } 149 - }); 150 - }); 165 + } 166 + } 151 167 }), 152 168 ), 153 169 increaseSkillToMax: (skillId: number, jobId: number) => 154 170 set( 155 171 produce((state: State) => { 156 172 const job = getJobById(jobId, state.jobTree); 157 - const skill = getSkillById(skillId, job?.skills!); 158 - if (skill!.levels === skill!.skillLevel) return state; 159 - if (skill!.levels * skill!.skillLevel < state.skillPoints) { 160 - skill!.skillLevel = 161 - state.skillPoints / skill!.points > skill!.levels 162 - ? skill!.levels 163 - : Math.floor(+(state.skillPoints / skill!.points)); 164 - state.skillPoints -= skill!.skillLevel * skill!.points; 165 - } 173 + if (job) { 174 + const skill = getSkillById(skillId, job.skills); 166 175 167 - // refactor this block of code 168 - job?.skills.forEach((s) => { 169 - const foundSkill = s.requirements.find((sz) => sz.skill === skillId); 170 - const skillIndex = s.requirements.findIndex( 171 - (sx) => sx.skill === skillId, 172 - ); 173 - if ( 174 - typeof foundSkill !== "undefined" && 175 - (foundSkill.level < skill!.skillLevel || 176 - foundSkill.level === skill!.skillLevel) 177 - ) { 178 - s.requirements[skillIndex].hasMinLevel = true; 176 + if (skill) { 177 + if (skill.levels === skill.skillLevel) return state; 178 + if (skill.levels * skill.skillLevel < state.skillPoints) { 179 + skill.skillLevel = 180 + state.skillPoints / skill.points > skill.levels 181 + ? skill.levels 182 + : Math.floor(+(state.skillPoints / skill.points)); 183 + state.skillPoints -= skill.skillLevel * skill.points; 184 + } 179 185 } 180 - }); 186 + 187 + // refactor this block of code 188 + for (const s of job.skills) { 189 + const requiredSkill = s.requirements.find( 190 + (required) => required.skill === skillId, 191 + ); 192 + const requiredSkillIndex = s.requirements.findIndex( 193 + (required) => required.skill === skillId, 194 + ); 195 + if ( 196 + typeof requiredSkill !== "undefined" && 197 + (requiredSkill.level < s.skillLevel || 198 + requiredSkill.level === s.skillLevel) 199 + ) { 200 + s.requirements[requiredSkillIndex].hasMinLevel = true; 201 + } 202 + } 203 + } 181 204 }), 182 205 ), 183 206 resetSkillTree: (jobId: number) => 184 207 set((state: State) => { 185 - let skillPoints = getJobTotalSkillPoints( 208 + const skillPoints = getJobTotalSkillPoints( 186 209 state.classSkillPoints, 187 210 jobId, 188 211 15,