+45
-32
apps/skillulator/src/routes/c/$class/components/Skill.tsx
+45
-32
apps/skillulator/src/routes/c/$class/components/Skill.tsx
···
99
99
</button>
100
100
</div>
101
101
<div className="flex gap-4">
102
-
{props?.masterVariations?.map((variation) => (
103
-
<div
104
-
key={variation.id}
105
-
data-skill={props.skill.name}
106
-
className="relative flex flex-col items-center flex-1 py-2 bg-white border border-gray-300 rounded-md basis-1/2 min-content"
107
-
>
108
-
<SkillIconButton
109
-
locale={props.lang}
110
-
skill={{ ...variation, id: props.skill.id }}
111
-
variations={props?.masterVariations}
112
-
masterVariationSkillId={variation?.id}
113
-
jobId={props.jobId}
114
-
hasMinLevelRequirements={variation?.hasMinLevelRequirements}
115
-
isMaxed={variation?.isMaxed}
116
-
/>
117
-
<div>
118
-
{variation.requirements.map((variation, index: number) => (
119
-
<Requirement
120
-
key={JSON.stringify({ variation, index })}
121
-
hasMinLevelRequirements={variation.hasMinLevel}
122
-
skill={{ level: variation.level, name: variation.name }}
123
-
/>
124
-
))}
102
+
{props?.masterVariations?.map((variation, index) => {
103
+
const selectedMasterVariation = props?.masterVariations?.some(
104
+
(s) => s.isSelected === true,
105
+
)
106
+
? props?.masterVariations?.filter((s) => s.isSelected === true)
107
+
: props.masterVariations;
108
+
109
+
const isSelectedMasterVariation =
110
+
!selectedMasterVariation?.every((v) => v.id === variation.id) &&
111
+
selectedMasterVariation?.length === 1;
112
+
113
+
return (
114
+
<div
115
+
key={JSON.stringify({ id: variation.id, index })}
116
+
data-skill={props.skill.name}
117
+
className="relative flex flex-col items-center flex-1 py-2 bg-white border border-gray-300 rounded-md basis-1/2 min-content"
118
+
>
119
+
<SkillIconButton
120
+
locale={props.lang}
121
+
skill={{ ...variation, id: props.skill.id }}
122
+
masterVariationSkillId={variation?.id}
123
+
isSelectedMasterVariation={isSelectedMasterVariation}
124
+
jobId={props.jobId}
125
+
hasMinLevelRequirements={variation?.hasMinLevelRequirements}
126
+
isMaxed={variation?.isMaxed}
127
+
/>
128
+
<div>
129
+
{variation.requirements.map((variation, index: number) => (
130
+
<Requirement
131
+
key={JSON.stringify({ variation, index })}
132
+
hasMinLevelRequirements={variation.hasMinLevel}
133
+
skill={{ level: variation.level, name: variation.name }}
134
+
/>
135
+
))}
136
+
</div>
137
+
{/* <DecreaseSkillPointButton {...props} /> */}
138
+
<IncreaseSkillToMaxButton
139
+
skillId={variation.id}
140
+
jobId={props.jobId}
141
+
isSelectedMasterVariation={isSelectedMasterVariation}
142
+
hasMinLevelRequirements={variation?.hasMinLevelRequirements}
143
+
isMaxed={variation?.isMaxed}
144
+
/>
125
145
</div>
126
-
{/* <DecreaseSkillPointButton {...props} /> */}
127
-
<IncreaseSkillToMaxButton
128
-
skillId={variation.id}
129
-
jobId={props.jobId}
130
-
hasMinLevelRequirements={variation?.hasMinLevelRequirements}
131
-
isMaxed={variation?.isMaxed}
132
-
/>
133
-
</div>
134
-
))}
146
+
);
147
+
})}
135
148
</div>
136
149
</dialog>
137
150
);
+178
-166
apps/skillulator/src/zustand/treeStore.ts
+178
-166
apps/skillulator/src/zustand/treeStore.ts
···
1
1
import { produce } from "immer";
2
2
import { create } from "zustand";
3
+
import { immer } from "zustand/middleware/immer";
3
4
import { tree as jobTree } from "@/data/tree";
4
5
import {
5
6
getJobById,
···
50
51
skillPoints: 0,
51
52
};
52
53
53
-
export const useTreeStore = create<State & Actions>()((set, get) => ({
54
-
jobTree,
55
-
JOB_SKILLPOINTS,
56
-
skillPoints: 0,
57
-
initSkillPoints: (jobId: number, characterLevel: number) =>
58
-
set(
59
-
produce((state: State) => {
60
-
// need to figure out how many skill points are already spent and subtract it
61
-
const totalSkillPoints = getJobTotalSkillPoints(
62
-
state.JOB_SKILLPOINTS,
63
-
jobId,
64
-
characterLevel,
65
-
);
66
-
67
-
const job = getJobById(jobId, state.jobTree);
68
-
if (!job) return state;
69
-
70
-
const spentSkillPoints = job.skills.reduce(
71
-
(total, skill) => total + skill.skillLevel * skill.points,
72
-
0,
73
-
);
74
-
75
-
const remainingSkillPoints = totalSkillPoints - spentSkillPoints;
76
-
77
-
state.skillPoints = remainingSkillPoints;
78
-
return state;
79
-
}),
80
-
),
81
-
increaseSkillPoint: ({
82
-
jobId,
83
-
skillId,
84
-
masterVariationSkillId,
85
-
}: { jobId: number; skillId: number; masterVariationSkillId?: number }) =>
86
-
set(
87
-
produce((state: State) => {
88
-
const job = getJobById(jobId, state.jobTree);
89
-
if (!job) return state;
90
-
91
-
/**
92
-
* Master variation skills don't live in the outer skill array, only within the skill they are variations of...
93
-
*/
94
-
const skill = getSkillById(skillId, job.skills);
95
-
if (
96
-
skill?.masterVariations?.length &&
97
-
typeof masterVariationSkillId !== "undefined"
98
-
) {
99
-
if (!masterVariationSkillId) return state;
100
-
const masterVariationSkill = getSkillById(
101
-
masterVariationSkillId,
102
-
skill.masterVariations,
54
+
export const useTreeStore = create<State & Actions>()(
55
+
immer((set) => ({
56
+
jobTree,
57
+
JOB_SKILLPOINTS,
58
+
skillPoints: 0,
59
+
initSkillPoints: (jobId: number, characterLevel: number) =>
60
+
set(
61
+
produce((state: State) => {
62
+
// need to figure out how many skill points are already spent and subtract it
63
+
const totalSkillPoints = getJobTotalSkillPoints(
64
+
state.JOB_SKILLPOINTS,
65
+
jobId,
66
+
characterLevel,
103
67
);
104
68
105
-
if (!masterVariationSkill) return state;
69
+
const job = getJobById(jobId, state.jobTree);
70
+
if (!job) return state;
106
71
107
-
if (masterVariationSkill.skillLevel === masterVariationSkill.levels)
108
-
return state;
109
-
if (masterVariationSkill.points > state.skillPoints) return state;
72
+
const spentSkillPoints = job.skills.reduce(
73
+
(total, skill) => total + skill.skillLevel * skill.points,
74
+
0,
75
+
);
110
76
111
-
masterVariationSkill.skillLevel += 1;
112
-
state.skillPoints -= masterVariationSkill.points;
77
+
const remainingSkillPoints = totalSkillPoints - spentSkillPoints;
113
78
114
-
updateSkillRequirements(
115
-
job,
116
-
masterVariationSkillId,
117
-
masterVariationSkill.skillLevel,
118
-
);
79
+
state.skillPoints = remainingSkillPoints;
119
80
return state;
120
-
}
81
+
}),
82
+
),
83
+
increaseSkillPoint: ({
84
+
jobId,
85
+
skillId,
86
+
masterVariationSkillId,
87
+
}: {
88
+
jobId: number;
89
+
skillId: number;
90
+
masterVariationSkillId?: number;
91
+
}) =>
92
+
set(
93
+
produce((state: State) => {
94
+
const job = getJobById(jobId, state.jobTree);
95
+
if (!job) return state;
121
96
122
-
if (!skill) return state;
97
+
/**
98
+
* Master variation skills don't live in the outer skill array, only within the skill they are variations of...
99
+
*/
100
+
const skill = getSkillById(skillId, job.skills);
123
101
124
-
if (skill.skillLevel === skill.levels) return state;
125
-
if (skill.points > state.skillPoints) return state;
102
+
if (
103
+
skill?.masterVariations?.length &&
104
+
typeof masterVariationSkillId !== "undefined"
105
+
) {
106
+
if (!masterVariationSkillId) return state;
107
+
const masterVariationSkill = getSkillById(
108
+
masterVariationSkillId,
109
+
skill.masterVariations,
110
+
);
126
111
127
-
skill.skillLevel += 1;
128
-
state.skillPoints -= skill.points;
112
+
if (!masterVariationSkill) return state;
129
113
130
-
updateSkillRequirements(job, skillId, skill.skillLevel);
131
-
return state;
132
-
}),
133
-
),
134
-
decreaseSkillPoint: ({
135
-
jobId,
136
-
skillId,
137
-
masterVariationSkillId,
138
-
}: { jobId: number; skillId: number; masterVariationSkillId?: number }) =>
139
-
set(
140
-
produce((state: State) => {
141
-
const job = getJobById(jobId, state.jobTree);
142
-
if (!job) return state;
114
+
if (masterVariationSkill.skillLevel === masterVariationSkill.levels)
115
+
return state;
116
+
if (masterVariationSkill.points > state.skillPoints) return state;
143
117
144
-
const skill = getSkillById(skillId, job.skills);
145
-
if (
146
-
skill?.masterVariations?.length &&
147
-
typeof masterVariationSkillId !== "undefined"
148
-
) {
149
-
if (!masterVariationSkillId) return state;
150
-
const masterVariationSkill = getSkillById(
151
-
masterVariationSkillId,
152
-
skill.masterVariations,
153
-
);
118
+
masterVariationSkill.skillLevel += 1;
119
+
state.skillPoints -= masterVariationSkill.points;
154
120
155
-
if (!masterVariationSkill) return state;
156
-
if (masterVariationSkill.skillLevel === 0) {
121
+
updateSkillRequirements(
122
+
job,
123
+
masterVariationSkillId,
124
+
masterVariationSkill.skillLevel,
125
+
);
157
126
return state;
158
127
}
159
128
160
-
masterVariationSkill.skillLevel -= 1;
161
-
state.skillPoints += masterVariationSkill.points;
129
+
if (!skill) return state;
162
130
163
-
updateSkillRequirements(
164
-
job,
165
-
masterVariationSkillId,
166
-
masterVariationSkill.skillLevel,
167
-
);
168
-
return state;
169
-
}
131
+
if (skill.skillLevel === skill.levels) return state;
132
+
if (skill.points > state.skillPoints) return state;
170
133
171
-
if (!skill || skill.skillLevel === 0) return state;
134
+
skill.skillLevel += 1;
135
+
state.skillPoints -= skill.points;
172
136
173
-
skill.skillLevel -= 1;
174
-
state.skillPoints += skill.points;
137
+
updateSkillRequirements(job, skillId, skill.skillLevel);
138
+
return state;
139
+
}),
140
+
),
141
+
decreaseSkillPoint: ({
142
+
jobId,
143
+
skillId,
144
+
masterVariationSkillId,
145
+
}: {
146
+
jobId: number;
147
+
skillId: number;
148
+
masterVariationSkillId?: number;
149
+
}) =>
150
+
set(
151
+
produce((state: State) => {
152
+
const job = getJobById(jobId, state.jobTree);
153
+
if (!job) return state;
175
154
176
-
updateSkillRequirements(job, skillId, skill.skillLevel);
177
-
return state;
178
-
}),
179
-
),
180
-
createPreloadedSkillTree: (
181
-
jobId: number,
182
-
predefinedSkills: Array<Record<string, number>>,
183
-
) =>
184
-
set(
185
-
produce((state: State) => {
186
-
// we map over the pre-defined skills
187
-
// we check the skill id and level and update the skillLevel accordingly
188
-
// we also need to do the check to see if the skill is the min level
189
-
const job = getJobById(jobId, state.jobTree);
190
-
if (!job) return state;
155
+
const skill = getSkillById(skillId, job.skills);
156
+
if (
157
+
skill?.masterVariations?.length &&
158
+
typeof masterVariationSkillId !== "undefined"
159
+
) {
160
+
if (!masterVariationSkillId) return state;
161
+
const masterVariationSkill = getSkillById(
162
+
masterVariationSkillId,
163
+
skill.masterVariations,
164
+
);
191
165
192
-
for (const originalTreeSkill of job.skills) {
193
-
for (const predefinedTreeSkill of predefinedSkills) {
194
-
if (originalTreeSkill.id === predefinedTreeSkill.skill) {
195
-
originalTreeSkill.skillLevel = predefinedTreeSkill.level;
166
+
if (!masterVariationSkill) return state;
167
+
if (masterVariationSkill.skillLevel === 0) {
168
+
return state;
196
169
}
197
-
}
170
+
171
+
masterVariationSkill.skillLevel -= 1;
172
+
state.skillPoints += masterVariationSkill.points;
198
173
199
-
const skill = getSkillById(originalTreeSkill.id, job.skills);
200
-
if (skill) {
201
174
updateSkillRequirements(
202
175
job,
203
-
originalTreeSkill.id,
204
-
skill.skillLevel,
176
+
masterVariationSkillId,
177
+
masterVariationSkill.skillLevel,
205
178
);
179
+
return state;
206
180
}
207
-
}
208
-
return state;
209
-
}),
210
-
),
211
-
increaseSkillToMax: (skillId: number, jobId: number) =>
212
-
set(
213
-
produce((state: State) => {
214
-
const job = getJobById(jobId, state.jobTree);
215
-
if (!job) return state;
216
181
217
-
const skill = getSkillById(skillId, job.skills);
218
-
if (!skill) return state;
182
+
if (!skill || skill.skillLevel === 0) return state;
219
183
220
-
if (skill.levels === skill.skillLevel) return state;
184
+
skill.skillLevel -= 1;
185
+
state.skillPoints += skill.points;
221
186
222
-
const maxPossibleLevel = Math.min(
223
-
skill.levels,
224
-
Math.floor(state.skillPoints / skill.points),
225
-
);
187
+
updateSkillRequirements(job, skillId, skill.skillLevel);
188
+
return state;
189
+
}),
190
+
),
191
+
createPreloadedSkillTree: (
192
+
jobId: number,
193
+
predefinedSkills: Array<Record<string, number>>,
194
+
) =>
195
+
set(
196
+
produce((state: State) => {
197
+
// we map over the pre-defined skills
198
+
// we check the skill id and level and update the skillLevel accordingly
199
+
// we also need to do the check to see if the skill is the min level
200
+
const job = getJobById(jobId, state.jobTree);
201
+
if (!job) return state;
226
202
227
-
if (maxPossibleLevel > skill.skillLevel) {
228
-
const levelsToAdd = maxPossibleLevel - skill.skillLevel;
229
-
skill.skillLevel = maxPossibleLevel;
230
-
state.skillPoints -= levelsToAdd * skill.points;
231
-
}
203
+
for (const originalTreeSkill of job.skills) {
204
+
for (const predefinedTreeSkill of predefinedSkills) {
205
+
if (originalTreeSkill.id === predefinedTreeSkill.skill) {
206
+
originalTreeSkill.skillLevel = predefinedTreeSkill.level;
207
+
}
208
+
}
232
209
233
-
updateSkillRequirements(job, skillId, skill.skillLevel);
234
-
return state;
235
-
}),
236
-
),
237
-
resetSkillTree: (jobId: number) =>
238
-
set((state: State) => {
239
-
const skillPoints = getJobTotalSkillPoints(
240
-
state.JOB_SKILLPOINTS,
241
-
jobId,
242
-
15,
243
-
);
210
+
const skill = getSkillById(originalTreeSkill.id, job.skills);
211
+
if (skill) {
212
+
updateSkillRequirements(
213
+
job,
214
+
originalTreeSkill.id,
215
+
skill.skillLevel,
216
+
);
217
+
}
218
+
}
219
+
return state;
220
+
}),
221
+
),
222
+
increaseSkillToMax: (skillId: number, jobId: number) =>
223
+
set(
224
+
produce((state: State) => {
225
+
const job = getJobById(jobId, state.jobTree);
226
+
if (!job) return state;
244
227
245
-
return {
246
-
...initialState,
247
-
skillPoints,
248
-
};
249
-
}),
250
-
}));
228
+
const skill = getSkillById(skillId, job.skills);
229
+
if (!skill) return state;
230
+
231
+
if (skill.levels === skill.skillLevel) return state;
232
+
233
+
const maxPossibleLevel = Math.min(
234
+
skill.levels,
235
+
Math.floor(state.skillPoints / skill.points),
236
+
);
237
+
238
+
if (maxPossibleLevel > skill.skillLevel) {
239
+
const levelsToAdd = maxPossibleLevel - skill.skillLevel;
240
+
skill.skillLevel = maxPossibleLevel;
241
+
state.skillPoints -= levelsToAdd * skill.points;
242
+
}
243
+
244
+
updateSkillRequirements(job, skillId, skill.skillLevel);
245
+
return state;
246
+
}),
247
+
),
248
+
resetSkillTree: (jobId: number) =>
249
+
set((state: State) => {
250
+
const skillPoints = getJobTotalSkillPoints(
251
+
state.JOB_SKILLPOINTS,
252
+
jobId,
253
+
15,
254
+
);
255
+
256
+
return {
257
+
...initialState,
258
+
skillPoints,
259
+
};
260
+
}),
261
+
})),
262
+
);