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

Configure Feed

Select the types of activity you want to include in your feed.

at main 332 lines 8.8 kB view raw
1import { produce } from "immer"; 2import { create } from "zustand"; 3import { immer } from "zustand/middleware/immer"; 4import { tree as jobTree } from "@/data/tree"; 5import { 6 getJobById, 7 getJobTotalSkillPoints, 8 getSkillById, 9 updateSkillRequirements, 10} from "@/utils/skill-tree-helpers"; 11import { JOB_SKILLPOINTS } from "@/utils/constants"; 12 13export type State = { 14 jobTree: typeof jobTree; 15 skillPoints: number; 16 JOB_SKILLPOINTS: typeof JOB_SKILLPOINTS; 17}; 18 19type Actions = { 20 increaseSkillPoint: ({ 21 jobId, 22 skillId, 23 masterVariationSkillId, 24 }: { 25 jobId: number; 26 skillId: number; 27 masterVariationSkillId?: number; 28 }) => void; 29 increaseSkillToMax: ({ 30 jobId, 31 skillId, 32 masterVariationSkillId, 33 }: { 34 jobId: number; 35 skillId: number; 36 masterVariationSkillId?: number; 37 }) => void; 38 decreaseSkillPoint: ({ 39 jobId, 40 skillId, 41 masterVariationSkillId, 42 }: { 43 jobId: number; 44 skillId: number; 45 masterVariationSkillId?: number; 46 }) => void; 47 initSkillPoints: (jobId: number, characterLevel: number) => void; 48 49 createPreloadedSkillTree: ( 50 jobId: number, 51 skills: Array<Record<string, number>>, 52 ) => void; 53 resetSkillTree: (jobId: number) => void; 54}; 55 56const initialState: State = { 57 jobTree, 58 JOB_SKILLPOINTS, 59 skillPoints: 0, 60}; 61 62export const useTreeStore = create<State & Actions>()( 63 immer((set) => ({ 64 jobTree, 65 JOB_SKILLPOINTS, 66 skillPoints: 0, 67 initSkillPoints: (jobId: number, characterLevel: number) => 68 set( 69 produce((state: State) => { 70 // need to figure out how many skill points are already spent and subtract it 71 const totalSkillPoints = getJobTotalSkillPoints( 72 state.JOB_SKILLPOINTS, 73 jobId, 74 characterLevel, 75 ); 76 77 const job = getJobById(jobId, state.jobTree); 78 if (!job) return state; 79 80 // i didn't write this but it works so w/e 81 const spentSkillPoints = job.skills.reduce((total, skill) => { 82 const base = (skill.skillLevel || 0) * (skill.points || 0); 83 const mv = 84 skill.masterVariations?.reduce( 85 (sum, mvSkill) => 86 sum + (mvSkill.skillLevel || 0) * (mvSkill.points || 0), 87 0, 88 ) || 0; 89 return total + base + mv; 90 }, 0); 91 92 const remainingSkillPoints = totalSkillPoints - spentSkillPoints; 93 94 state.skillPoints = remainingSkillPoints; 95 return state; 96 }), 97 ), 98 increaseSkillPoint: ({ 99 jobId, 100 skillId, 101 masterVariationSkillId, 102 }: { 103 jobId: number; 104 skillId: number; 105 masterVariationSkillId?: number; 106 }) => 107 set( 108 produce((state: State) => { 109 const job = getJobById(jobId, state.jobTree); 110 if (!job) return state; 111 112 /** 113 * Master variation skills don't live in the outer skill array, only within the skill they are variations of... 114 */ 115 const skill = getSkillById(skillId, job.skills); 116 117 if ( 118 skill?.masterVariations?.length && 119 typeof masterVariationSkillId !== "undefined" 120 ) { 121 if (!masterVariationSkillId) return state; 122 const masterVariationSkill = getSkillById( 123 masterVariationSkillId, 124 skill.masterVariations, 125 ); 126 127 if (!masterVariationSkill) return state; 128 129 if (masterVariationSkill.skillLevel === masterVariationSkill.levels) 130 return state; 131 if (masterVariationSkill.points > state.skillPoints) return state; 132 133 masterVariationSkill.skillLevel += 1; 134 state.skillPoints -= masterVariationSkill.points; 135 136 updateSkillRequirements( 137 job, 138 masterVariationSkillId, 139 masterVariationSkill.skillLevel, 140 ); 141 return state; 142 } 143 144 if (!skill) return state; 145 146 if (skill.skillLevel === skill.levels) return state; 147 if (skill.points > state.skillPoints) return state; 148 149 skill.skillLevel += 1; 150 state.skillPoints -= skill.points; 151 152 updateSkillRequirements(job, skillId, skill.skillLevel); 153 return state; 154 }), 155 ), 156 decreaseSkillPoint: ({ 157 jobId, 158 skillId, 159 masterVariationSkillId, 160 }: { 161 jobId: number; 162 skillId: number; 163 masterVariationSkillId?: number; 164 }) => 165 set( 166 produce((state: State) => { 167 const job = getJobById(jobId, state.jobTree); 168 if (!job) return state; 169 170 const skill = getSkillById(skillId, job.skills); 171 if ( 172 skill?.masterVariations?.length && 173 typeof masterVariationSkillId !== "undefined" 174 ) { 175 if (!masterVariationSkillId) return state; 176 const masterVariationSkill = getSkillById( 177 masterVariationSkillId, 178 skill.masterVariations, 179 ); 180 181 if (!masterVariationSkill) return state; 182 if (masterVariationSkill.skillLevel === 0) { 183 return state; 184 } 185 186 masterVariationSkill.skillLevel -= 1; 187 state.skillPoints += masterVariationSkill.points; 188 189 updateSkillRequirements( 190 job, 191 masterVariationSkillId, 192 masterVariationSkill.skillLevel, 193 ); 194 return state; 195 } 196 197 if (!skill || skill.skillLevel === 0) return state; 198 199 skill.skillLevel -= 1; 200 state.skillPoints += skill.points; 201 202 updateSkillRequirements(job, skillId, skill.skillLevel); 203 return state; 204 }), 205 ), 206 createPreloadedSkillTree: ( 207 jobId: number, 208 predefinedSkills: Array<Record<string, number>>, 209 ) => 210 set( 211 produce((state: State) => { 212 // we map over the pre-defined skills 213 // we check the skill id and level and update the skillLevel accordingly 214 // we also need to do the check to see if the skill is the min level 215 const job = getJobById(jobId, state.jobTree); 216 if (!job) return state; 217 218 for (const originalTreeSkill of job.skills) { 219 // set level for the base skill if present in predefinedSkills 220 for (const predefinedTreeSkill of predefinedSkills) { 221 if (originalTreeSkill.id === predefinedTreeSkill.skill) { 222 originalTreeSkill.skillLevel = predefinedTreeSkill.level; 223 } 224 } 225 226 // also handle any master variation skills that live inside this skill 227 if (originalTreeSkill.masterVariations?.length) { 228 for (const mvSkill of originalTreeSkill.masterVariations) { 229 for (const predefinedTreeSkill of predefinedSkills) { 230 if (mvSkill.id === predefinedTreeSkill.skill) { 231 mvSkill.skillLevel = predefinedTreeSkill.level; 232 } 233 } 234 // update requirements for this master variation skill 235 updateSkillRequirements(job, mvSkill.id, mvSkill.skillLevel); 236 } 237 } 238 239 const skill = getSkillById(originalTreeSkill.id, job.skills); 240 if (skill) { 241 updateSkillRequirements( 242 job, 243 originalTreeSkill.id, 244 skill.skillLevel, 245 ); 246 } 247 } 248 return state; 249 }), 250 ), 251 increaseSkillToMax: ({ 252 jobId, 253 skillId, 254 masterVariationSkillId, 255 }: { 256 jobId: number; 257 skillId: number; 258 masterVariationSkillId?: number; 259 }) => 260 set( 261 produce((state: State) => { 262 const job = getJobById(jobId, state.jobTree); 263 if (!job) return state; 264 265 const skill = getSkillById(skillId, job.skills); 266 if ( 267 skill?.masterVariations?.length && 268 typeof masterVariationSkillId !== "undefined" 269 ) { 270 if (!masterVariationSkillId) return state; 271 const masterVariationSkill = getSkillById( 272 masterVariationSkillId, 273 skill.masterVariations, 274 ); 275 276 if (!masterVariationSkill) return state; 277 if (masterVariationSkill.levels === masterVariationSkill.skillLevel) 278 return state; 279 280 const maxPossibleLevel = Math.min( 281 masterVariationSkill.levels, 282 Math.floor(state.skillPoints / masterVariationSkill.points), 283 ); 284 285 if (maxPossibleLevel > masterVariationSkill.skillLevel) { 286 const levelsToAdd = 287 maxPossibleLevel - masterVariationSkill.skillLevel; 288 masterVariationSkill.skillLevel = maxPossibleLevel; 289 state.skillPoints -= levelsToAdd * masterVariationSkill.points; 290 } 291 292 updateSkillRequirements( 293 job, 294 masterVariationSkillId, 295 masterVariationSkill.skillLevel, 296 ); 297 return state; 298 } 299 if (!skill) return state; 300 301 if (skill.levels === skill.skillLevel) return state; 302 303 const maxPossibleLevel = Math.min( 304 skill.levels, 305 Math.floor(state.skillPoints / skill.points), 306 ); 307 308 if (maxPossibleLevel > skill.skillLevel) { 309 const levelsToAdd = maxPossibleLevel - skill.skillLevel; 310 skill.skillLevel = maxPossibleLevel; 311 state.skillPoints -= levelsToAdd * skill.points; 312 } 313 314 updateSkillRequirements(job, skillId, skill.skillLevel); 315 return state; 316 }), 317 ), 318 resetSkillTree: (jobId: number) => 319 set((state: State) => { 320 const skillPoints = getJobTotalSkillPoints( 321 state.JOB_SKILLPOINTS, 322 jobId, 323 15, 324 ); 325 326 return { 327 ...initialState, 328 skillPoints, 329 }; 330 }), 331 })), 332);