+3
README.md
+3
README.md
+11
-1
apps/skillulator/README.md
+11
-1
apps/skillulator/README.md
···
1
1
# Skillulator - FlyFF Universe Skill Calculator
2
2
3
-
Skillulator is a skill calculator for the MMORPG FlyFF Universe. It is a web application that allows players to plan their character's skill build by selecting the skills they want to learn. Players can also share their builds with others by generating a unique URL that contains their build data.
3
+
Skillulator is a skill calculator for the MMORPG FlyFF Universe. It is a web application that allows players to plan their character's skill build by selecting the skills they want to learn. Players can also share their builds with others by generating a unique URL that contains their build data.
4
+
5
+
# V1.5 release
6
+
7
+
- [x] Added new level 166+ skills/master skills and variations
8
+
- [x] Added master skill variation selection
9
+
- [x] Increased level cap
10
+
- [x] Added new skill point calculation up to level 190
11
+
- [] Added tooltips on hover to all skils (long press for mobile devices)
12
+
- [] Picked master skills are displayed under base master skill
13
+
- [] Passive skills are marked as such in the UI
+131
-130
apps/skillulator/src/routes/c/$class/components/Skill.tsx
+131
-130
apps/skillulator/src/routes/c/$class/components/Skill.tsx
···
2
2
import { languages } from "@/utils/constants";
3
3
import { getLanguageForSkill } from "@/utils/language";
4
4
import {
5
-
// DecreaseSkillPointButton,
6
-
IncreaseSkillToMaxButton,
7
-
SkillIconButton,
5
+
// DecreaseSkillPointButton,
6
+
IncreaseSkillToMaxButton,
7
+
SkillIconButton,
8
8
} from "./action-buttons";
9
9
import { Requirement } from "./Requirement";
10
10
import { useRef, type ComponentRef, type RefObject } from "react";
11
11
12
12
interface SkillProps {
13
-
skill: SkillType;
14
-
jobId: number | undefined;
15
-
skillId: number;
16
-
hasMinLevelRequirements: boolean;
17
-
isMaxed: boolean;
18
-
lang: string;
13
+
skill: SkillType;
14
+
jobId: number | undefined;
15
+
skillId: number;
16
+
hasMinLevelRequirements: boolean;
17
+
isMaxed: boolean;
18
+
lang: string;
19
19
}
20
20
21
21
export default function Skill(props: SkillProps) {
22
-
const locale = getLanguageForSkill(languages, props.lang);
23
-
const skillName = props.skill.name.en
24
-
.replaceAll(" ", "")
25
-
.replace(/'/, "")
26
-
.replace(/[\[\]()]/g, "");
22
+
const locale = getLanguageForSkill(languages, props.lang);
23
+
const skillName = props.skill.name.en
24
+
.replaceAll(" ", "")
25
+
.replace(/'/, "")
26
+
.replace(/[\[\]()]/g, "");
27
27
28
-
const isMasterVariationSkill =
29
-
typeof props?.skill?.masterVariations?.length !== "undefined";
28
+
const isMasterVariationSkill =
29
+
typeof props?.skill?.masterVariations?.length !== "undefined";
30
30
31
-
const masterDialogRef = useRef<ComponentRef<"dialog">>(null);
31
+
const masterDialogRef = useRef<ComponentRef<"dialog">>(null);
32
+
console.log({ props })
32
33
33
-
return (
34
-
<div
35
-
data-skill={skillName}
36
-
className="relative flex flex-col items-center flex-1 py-2 bg-white border border-gray-300 rounded-md basis-1/2 min-content"
37
-
>
38
-
<SkillIconButton {...props} locale={locale} />
39
-
<div>
40
-
{props.skill.requirements.map((skill, index: number) => (
41
-
<Requirement
42
-
key={JSON.stringify({ skill, index })}
43
-
hasMinLevelRequirements={skill.hasMinLevel}
44
-
skill={{ level: skill.level, name: skill.name }}
45
-
/>
46
-
))}
47
-
</div>
48
-
<div>
49
-
<button
50
-
type="button"
51
-
aria-label="open master variation dialog"
52
-
onClick={() => masterDialogRef?.current?.showModal()}
53
-
>
54
-
{isMasterVariationSkill ? (
55
-
<img
56
-
src="https://api.flyff.com/image/badge/master6.png"
57
-
alt=""
58
-
className="h-6 w-6 absolute top-2 left-3 cursor-pointer"
59
-
/>
60
-
) : null}
61
-
</button>
62
-
{/* <DecreaseSkillPointButton {...props} /> */}
63
-
<IncreaseSkillToMaxButton {...props} />
64
-
</div>
65
-
<MasterSkillVariationDialog
66
-
ref={masterDialogRef}
67
-
masterVariations={props?.skill?.masterVariations}
68
-
{...props}
69
-
/>
70
-
</div>
71
-
);
34
+
return (
35
+
<div
36
+
data-skill={skillName}
37
+
className="relative flex flex-col items-center flex-1 py-2 bg-white border border-gray-300 rounded-md basis-1/2 min-content"
38
+
>
39
+
<SkillIconButton {...props} locale={locale} />
40
+
<div>
41
+
{props.skill.requirements.map((skill, index: number) => (
42
+
<Requirement
43
+
key={JSON.stringify({ skill, index })}
44
+
hasMinLevelRequirements={skill.hasMinLevel}
45
+
skill={{ level: skill.level, name: skill.name }}
46
+
/>
47
+
))}
48
+
</div>
49
+
<div>
50
+
<button
51
+
type="button"
52
+
aria-label="open master variation dialog"
53
+
onClick={() => masterDialogRef?.current?.showModal()}
54
+
>
55
+
{isMasterVariationSkill ? (
56
+
<img
57
+
src="https://api.flyff.com/image/badge/master6.png"
58
+
alt=""
59
+
className="h-6 w-6 absolute top-2 left-3 cursor-pointer"
60
+
/>
61
+
) : null}
62
+
</button>
63
+
{/* <DecreaseSkillPointButton {...props} /> */}
64
+
<IncreaseSkillToMaxButton {...props} />
65
+
</div>
66
+
<MasterSkillVariationDialog
67
+
ref={masterDialogRef}
68
+
masterVariations={props?.skill?.masterVariations}
69
+
{...props}
70
+
/>
71
+
</div>
72
+
);
72
73
}
73
74
74
75
function MasterSkillVariationDialog(props: {
75
-
ref: RefObject<HTMLDialogElement | null>;
76
-
masterVariations: SkillType["masterVariations"];
77
-
skill: SkillType;
78
-
jobId: number | undefined;
79
-
skillId: number;
80
-
hasMinLevelRequirements: boolean;
81
-
isMaxed: boolean;
82
-
lang: string;
76
+
ref: RefObject<HTMLDialogElement | null>;
77
+
masterVariations: SkillType["masterVariations"];
78
+
skill: SkillType;
79
+
jobId: number | undefined;
80
+
skillId: number;
81
+
hasMinLevelRequirements: boolean;
82
+
isMaxed: boolean;
83
+
lang: string;
83
84
}) {
84
-
return (
85
-
<dialog
86
-
ref={props.ref}
87
-
className="border border-gray-300 shadow-sm rounded-md p-5 w-1/2 mx-auto my-auto"
88
-
>
89
-
<div className="flex justify-between mb-4">
90
-
<h2 className="font-bold">
91
-
Select a Master Variation (you can only choose one)
92
-
</h2>
93
-
<button
94
-
type="button"
95
-
onClick={() => props?.ref?.current?.close()}
96
-
className="text-sm bg-red-500 text-white py-1 px-2 rounded-md cursor-pointer"
97
-
>
98
-
Close Dialog
99
-
</button>
100
-
</div>
101
-
<div className="flex gap-4">
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;
85
+
return (
86
+
<dialog
87
+
ref={props.ref}
88
+
className="border border-gray-300 shadow-sm rounded-md p-5 w-1/2 mx-auto my-auto"
89
+
>
90
+
<div className="flex justify-between mb-4">
91
+
<h2 className="font-bold">
92
+
Select a Master Variation (you can only choose one)
93
+
</h2>
94
+
<button
95
+
type="button"
96
+
onClick={() => props?.ref?.current?.close()}
97
+
className="text-sm bg-red-500 text-white py-1 px-2 rounded-md cursor-pointer"
98
+
>
99
+
Close Dialog
100
+
</button>
101
+
</div>
102
+
<div className="flex gap-4">
103
+
{props?.masterVariations?.map((variation, index) => {
104
+
const selectedMasterVariation = props?.masterVariations?.some(
105
+
(s) => s.isSelected === true,
106
+
)
107
+
? props?.masterVariations?.filter((s) => s.isSelected === true)
108
+
: props.masterVariations;
108
109
109
-
const isSelectedMasterVariation =
110
-
!selectedMasterVariation?.every((v) => v.id === variation.id) &&
111
-
selectedMasterVariation?.length === 1;
110
+
const isSelectedMasterVariation =
111
+
!selectedMasterVariation?.every((v) => v.id === variation.id) &&
112
+
selectedMasterVariation?.length === 1;
112
113
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={props.skill.id}
140
-
jobId={props.jobId}
141
-
isSelectedMasterVariation={isSelectedMasterVariation}
142
-
masterVariationSkillId={variation.id}
143
-
hasMinLevelRequirements={variation?.hasMinLevelRequirements}
144
-
isMaxed={variation?.isMaxed}
145
-
/>
146
-
</div>
147
-
);
148
-
})}
149
-
</div>
150
-
</dialog>
151
-
);
114
+
return (
115
+
<div
116
+
key={JSON.stringify({ id: variation.id, index })}
117
+
data-skill={props.skill.name}
118
+
className="relative flex flex-col items-center flex-1 py-2 bg-white border border-gray-300 rounded-md basis-1/2 min-content"
119
+
>
120
+
<SkillIconButton
121
+
locale={props.lang}
122
+
skill={{ ...variation, id: props.skill.id }}
123
+
masterVariationSkillId={variation?.id}
124
+
isSelectedMasterVariation={isSelectedMasterVariation}
125
+
jobId={props.jobId}
126
+
hasMinLevelRequirements={variation?.hasMinLevelRequirements}
127
+
isMaxed={variation?.isMaxed}
128
+
/>
129
+
<div>
130
+
{variation.requirements.map((variation, index: number) => (
131
+
<Requirement
132
+
key={JSON.stringify({ variation, index })}
133
+
hasMinLevelRequirements={variation.hasMinLevel}
134
+
skill={{ level: variation.level, name: variation.name }}
135
+
/>
136
+
))}
137
+
</div>
138
+
{/* <DecreaseSkillPointButton {...props} /> */}
139
+
<IncreaseSkillToMaxButton
140
+
skillId={props.skill.id}
141
+
jobId={props.jobId}
142
+
isSelectedMasterVariation={isSelectedMasterVariation}
143
+
masterVariationSkillId={variation.id}
144
+
hasMinLevelRequirements={variation?.hasMinLevelRequirements}
145
+
isMaxed={variation?.isMaxed}
146
+
/>
147
+
</div>
148
+
);
149
+
})}
150
+
</div>
151
+
</dialog>
152
+
);
152
153
}