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

add checklist for remaining items for v1.5 release

besaid.zone a740e6d3 ac164a8f

verified
Changed files
+145 -131
apps
skillulator
src
routes
c
$class
components
+3
README.md
··· 1 + # FlyFF Tools 2 + 3 + A collection of apps and tools I use to build stuff for Flyff Universe
+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
··· 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 }