diff --git a/.gitignore b/.gitignore index 34d553d3..29947561 100644 --- a/.gitignore +++ b/.gitignore @@ -51,6 +51,8 @@ public/images/weapon* public/images/summon* public/images/chara* public/images/job* +public/images/awakening* +public/images/ax* # Typescript v1 declaration files typings/ diff --git a/components/AwakeningSelect/index.scss b/components/AwakeningSelect/index.scss new file mode 100644 index 00000000..56c80b35 --- /dev/null +++ b/components/AwakeningSelect/index.scss @@ -0,0 +1,31 @@ +.AwakeningSelect .AwakeningSet { + .errors { + color: $error; + display: none; + padding: $unit 0; + + &.visible { + display: block; + } + } + + .fields { + display: flex; + flex-direction: row; + gap: $unit; + width: 100%; + + .SelectTrigger { + flex-grow: 1; + } + + .Label { + flex-grow: 0; + + .Input { + min-width: $unit * 12; + width: inherit; + } + } + } +} diff --git a/components/AwakeningSelect/index.tsx b/components/AwakeningSelect/index.tsx new file mode 100644 index 00000000..ed01ccfb --- /dev/null +++ b/components/AwakeningSelect/index.tsx @@ -0,0 +1,192 @@ +import React, { ForwardedRef, useEffect, useState } from 'react' +import { useRouter } from 'next/router' +import { useTranslation } from 'next-i18next' + +import Input from '~components/Input' +import Select from '~components/Select' +import SelectItem from '~components/SelectItem' + +import classNames from 'classnames' + +import { weaponAwakening, characterAwakening } from '~utils/awakening' +import type { Awakening } from '~utils/awakening' + +import './index.scss' + +interface Props { + object: 'character' | 'weapon' + awakeningType?: number + awakeningLevel?: number + sendValidity: (isValid: boolean) => void + sendValues: (type: number, level: number) => void +} + +const AwakeningSelect = (props: Props) => { + const router = useRouter() + const locale = + router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en' + const { t } = useTranslation('common') + + const [open, setOpen] = useState(false) + + // Refs + const awakeningLevelInput = React.createRef() + + // States + const [awakeningType, setAwakeningType] = useState(-1) + const [awakeningLevel, setAwakeningLevel] = useState(1) + + const [maxValue, setMaxValue] = useState(1) + + const [error, setError] = useState('') + + // Classes + const inputClasses = classNames({ + Bound: true, + Hidden: awakeningType === -1, + }) + + const errorClasses = classNames({ + errors: true, + visible: error !== '', + }) + + // Set max value based on object type + useEffect(() => { + if (props.object === 'character') setMaxValue(9) + else if (props.object === 'weapon') setMaxValue(15) + }, [props.object]) + + // Set default awakening and level based on object type + useEffect(() => { + let defaultAwakening = 0 + if (props.object === 'weapon') defaultAwakening = -1 + + setAwakeningType( + props.awakeningType != undefined ? props.awakeningType : defaultAwakening + ) + setAwakeningLevel(props.awakeningLevel ? props.awakeningLevel : 1) + }, [props.object, props.awakeningType, props.awakeningLevel]) + + // Send awakening type and level when changed + useEffect(() => { + props.sendValues(awakeningType, awakeningLevel) + }, [props.sendValues, awakeningType, awakeningLevel]) + + // Send validity of form when awakening level changes + useEffect(() => { + props.sendValidity(awakeningLevel > 0 && error === '') + }, [props.sendValidity, awakeningLevel, error]) + + // Classes + function changeOpen() { + setOpen(!open) + } + + function generateOptions(object: 'character' | 'weapon') { + let options: Awakening[] = [] + if (object === 'character') options = characterAwakening + else if (object === 'weapon') options = weaponAwakening + else return + + let optionElements: React.ReactNode[] = options.map((awakening, i) => { + return ( + + {awakening.name[locale]} + + ) + }) + + if (object === 'weapon') { + optionElements?.unshift( + + {t('awakening.no_type')} + + ) + } + + return optionElements + } + + function handleSelectChange(rawValue: string) { + const value = parseInt(rawValue) + setAwakeningType(value) + } + + function handleInputChange(event: React.ChangeEvent) { + const value = parseFloat(event.target.value) + if (handleLevelError(value)) setAwakeningLevel(value) + } + + function handleLevelError(value: number) { + let error = '' + if (value < 1) { + error = t('awakening.errors.value_too_low', { + minValue: 1, + }) + } else if (value > maxValue) { + error = t('awakening.errors.value_too_high', { + maxValue: maxValue, + }) + } else if (value % 1 != 0) { + error = t('awakening.errors.value_not_whole') + } else if (!value || value <= 0) { + error = t('awakening.errors.value_empty') + } else { + error = '' + } + + setError(error) + + return error.length === 0 + } + + const rangeString = (object: 'character' | 'weapon') => { + let minValue = 1 + let maxValue = 1 + + if (object === 'weapon') { + minValue = 1 + maxValue = 15 + } else if (object === 'character') { + minValue = 1 + maxValue = 9 + } else return + + return `${minValue}~${maxValue}` + } + + return ( +
+
+
+ + + +
+

{error}

+
+
+ ) +} + +export default AwakeningSelect diff --git a/components/AxSelect/index.scss b/components/AxSelect/index.scss index 98b5a435..1f1135b5 100644 --- a/components/AxSelect/index.scss +++ b/components/AxSelect/index.scss @@ -23,25 +23,24 @@ flex-direction: row; gap: $unit; - select { + .SelectTrigger { flex-grow: 1; margin: 0; } - .Input { + input { -webkit-font-smoothing: antialiased; border: none; - background-color: $grey-90; - border-radius: 6px; + border-radius: $input-corner; box-sizing: border-box; - color: $grey-15; - height: $unit * 6; - display: block; - font-size: $font-regular; - padding: $unit; + display: none; text-align: right; - min-width: 100px; + min-width: $unit-14x; width: 100px; + + &.Visible { + display: block; + } } } } diff --git a/components/AxSelect/index.tsx b/components/AxSelect/index.tsx index 44da8fed..28ee96f6 100644 --- a/components/AxSelect/index.tsx +++ b/components/AxSelect/index.tsx @@ -1,7 +1,10 @@ -import React, { useEffect, useState } from 'react' +import React, { ForwardedRef, useEffect, useState } from 'react' import { useRouter } from 'next/router' import { useTranslation } from 'next-i18next' +import Select from '~components/Select' +import SelectItem from '~components/SelectItem' + import classNames from 'classnames' import { axData } from '~utils/axData' @@ -32,6 +35,9 @@ const AXSelect = (props: Props) => { router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en' const { t } = useTranslation('common') + const [openAX1, setOpenAX1] = useState(false) + const [openAX2, setOpenAX2] = useState(false) + // Set up form states and error handling const [errors, setErrors] = useState({ axValue1: '', @@ -49,9 +55,9 @@ const AXSelect = (props: Props) => { }) // Refs - const primaryAxModifierSelect = React.createRef() + const primaryAxModifierSelect = React.createRef() const primaryAxValueInput = React.createRef() - const secondaryAxModifierSelect = React.createRef() + const secondaryAxModifierSelect = React.createRef() const secondaryAxValueInput = React.createRef() // States @@ -61,19 +67,8 @@ const AXSelect = (props: Props) => { const [secondaryAxValue, setSecondaryAxValue] = useState(0.0) useEffect(() => { - if (props.currentSkills && props.currentSkills[0]) { - if (props.currentSkills[0].modifier != null) - setPrimaryAxModifier(props.currentSkills[0].modifier) - - setPrimaryAxValue(props.currentSkills[0].strength) - } - - if (props.currentSkills && props.currentSkills[1]) { - if (props.currentSkills[1].modifier != null) - setSecondaryAxModifier(props.currentSkills[1].modifier) - - setSecondaryAxValue(props.currentSkills[1].strength) - } + setupAx1() + setupAx2() }, [props.currentSkills]) useEffect(() => { @@ -92,10 +87,68 @@ const AXSelect = (props: Props) => { ]) useEffect(() => { - props.sendValidity( - primaryAxValue > 0 && errors.axValue1 === '' && errors.axValue2 === '' + if ( + props.currentSkills && + props.currentSkills[0].modifier != null && + props.currentSkills[0].modifier >= 0 + ) { + setPrimaryAxModifier(props.currentSkills[0].modifier) + setPrimaryAxValue(props.currentSkills[0].strength) + } else setPrimaryAxModifier(-1) + }, [props.currentSkills, setPrimaryAxModifier]) + + useEffect(() => { + if (props.currentSkills && props.currentSkills[1].modifier) { + setSecondaryAxModifier(props.currentSkills[1].modifier) + setSecondaryAxValue(props.currentSkills[1].strength) + } else { + setSecondaryAxModifier(-1) + } + }, [props.currentSkills, setSecondaryAxModifier]) + + useEffect(() => { + console.log( + primaryAxModifier, + primaryAxValue, + secondaryAxModifier, + secondaryAxValue ) - }, [props, primaryAxValue, errors]) + + let noErrors = false + + if (errors.axValue1 === '' && errors.axValue2 === '') { + if (primaryAxModifier === -1 && secondaryAxModifier === -1) + noErrors = true + else if ( + primaryAxModifier >= 0 && + primaryAxValue > 0 && + secondaryAxModifier === -1 + ) + noErrors = true + else if ( + primaryAxModifier >= 0 && + primaryAxValue > 0 && + secondaryAxModifier >= 0 && + secondaryAxValue > 0 + ) + noErrors = true + else + console.log( + primaryAxModifier >= 0, + primaryAxValue > 0, + secondaryAxModifier >= 0, + secondaryAxValue > 0 + ) + } + + props.sendValidity(noErrors) + }, [ + primaryAxModifier, + primaryAxValue, + secondaryAxModifier, + secondaryAxValue, + errors, + ]) // Classes const secondarySetClasses = classNames({ @@ -103,6 +156,62 @@ const AXSelect = (props: Props) => { hidden: primaryAxModifier < 0, }) + function setupAx1() { + if ( + props.currentSkills && + props.currentSkills[0] && + // Should this be > -1 or != null + props.currentSkills[0].modifier != null + ) { + setPrimaryAxModifier(props.currentSkills[0].modifier) + setPrimaryAxValue(props.currentSkills[0].strength) + + if (props.currentSkills[0].modifier > -1 && primaryAxValueInput.current) { + const modifier = props.currentSkills[0].modifier + const axSkill = axData[props.axType - 1][modifier] + setupInput(axSkill, primaryAxValueInput.current) + } + } + } + + function setupAx2() { + if ( + props.currentSkills && + props.currentSkills[1] && + // Should this be > -1 or != null + props.currentSkills[1].modifier != null + ) { + const firstSkill = props.currentSkills[0] + const primaryAxSkill = axData[props.axType - 1][firstSkill.modifier] + const secondaryAxSkill = findSecondaryAxSkill( + primaryAxSkill, + props.currentSkills[1] + ) + + if ( + props.currentSkills[1].modifier > -1 && + secondaryAxValueInput.current + ) { + setupInput(secondaryAxSkill, secondaryAxValueInput.current) + } + } + } + + function findSecondaryAxSkill( + axSkill: AxSkill | undefined, + skillAtIndex: SimpleAxSkill + ) { + if (axSkill) + return axSkill.secondary + ? axSkill.secondary.find((skill) => skill.id === skillAtIndex.modifier) + : undefined + } + + function openSelect(ref: ForwardedRef) { + if (ref === primaryAxModifierSelect) setOpenAX1(!openAX1) + if (ref === secondaryAxModifierSelect) setOpenAX2(!openAX2) + } + function generateOptions(modifierSet: number) { const axOptions = axData[props.axType - 1] @@ -110,9 +219,9 @@ const AXSelect = (props: Props) => { if (modifierSet == 0) { axOptionElements = axOptions.map((ax, i) => { return ( - + ) }) } else { @@ -129,9 +238,9 @@ const AXSelect = (props: Props) => { const secondaryAxOptions = primarySkill.secondary axOptionElements = secondaryAxOptions.map((ax, i) => { return ( - + ) }) } @@ -139,39 +248,47 @@ const AXSelect = (props: Props) => { } axOptionElements?.unshift( - + ) return axOptionElements } - function handleSelectChange(event: React.ChangeEvent) { - const value = parseInt(event.target.value) + function handleAX1SelectChange(rawValue: string) { + const value = parseInt(rawValue) + setPrimaryAxModifier(value) - if (primaryAxModifierSelect.current == event.target) { - setPrimaryAxModifier(value) + if ( + primaryAxValueInput.current && + secondaryAxModifierSelect.current && + secondaryAxValueInput.current + ) { + setupInput(axData[props.axType - 1][value], primaryAxValueInput.current) + setPrimaryAxValue(0) + primaryAxValueInput.current.value = '' - if ( - primaryAxValueInput.current && - secondaryAxModifierSelect.current && - secondaryAxValueInput.current - ) { - setupInput(axData[props.axType - 1][value], primaryAxValueInput.current) + // Reset the secondary AX modifier, reset the AX value and hide the input + setSecondaryAxModifier(-1) + setSecondaryAxValue(0) + secondaryAxValueInput.current.className = 'Input Contained' + secondaryAxValueInput.current.value = '' + } + } - secondaryAxModifierSelect.current.value = '-1' - secondaryAxValueInput.current.value = '' - } - } else { - setSecondaryAxModifier(value) + function handleAX2SelectChange(rawValue: string) { + const value = parseInt(rawValue) + setSecondaryAxModifier(value) - const primaryAxSkill = axData[props.axType - 1][primaryAxModifier] - const currentAxSkill = primaryAxSkill.secondary - ? primaryAxSkill.secondary.find((skill) => skill.id == value) - : undefined + const primaryAxSkill = axData[props.axType - 1][primaryAxModifier] + const currentAxSkill = primaryAxSkill.secondary + ? primaryAxSkill.secondary.find((skill) => skill.id == value) + : undefined - if (secondaryAxValueInput.current) - setupInput(currentAxSkill, secondaryAxValueInput.current) + if (secondaryAxValueInput.current) { + setupInput(currentAxSkill, secondaryAxValueInput.current) + setSecondaryAxValue(0) + secondaryAxValueInput.current.value = '' } } @@ -199,7 +316,7 @@ const AXSelect = (props: Props) => { } else if (value > primaryAxSkill.maxValue) { newErrors.axValue1 = t('ax.errors.value_too_high', { name: primaryAxSkill.name[locale], - maxValue: primaryAxSkill.minValue, + maxValue: primaryAxSkill.maxValue, suffix: primaryAxSkill.suffix ? primaryAxSkill.suffix : '', }) } else if (!value || value <= 0) { @@ -234,7 +351,7 @@ const AXSelect = (props: Props) => { } else if (value > secondaryAxSkill.maxValue) { newErrors.axValue2 = t('ax.errors.value_too_high', { name: secondaryAxSkill.name[locale], - maxValue: secondaryAxSkill.minValue, + maxValue: secondaryAxSkill.maxValue, suffix: secondaryAxSkill.suffix ? secondaryAxSkill.suffix : '', }) } else if (!secondaryAxSkill.suffix && value % 1 !== 0) { @@ -260,6 +377,7 @@ const AXSelect = (props: Props) => { if (ax) { const rangeString = `${ax.minValue}~${ax.maxValue}${ax.suffix || ''}` + element.className = 'Input Bound Visible' element.disabled = false element.placeholder = rangeString element.min = `${ax.minValue}` @@ -268,10 +386,12 @@ const AXSelect = (props: Props) => { } else { if (primaryAxValueInput.current && secondaryAxValueInput.current) { if (primaryAxValueInput.current == element) { + primaryAxValueInput.current.className = 'Input Contained' primaryAxValueInput.current.disabled = true primaryAxValueInput.current.placeholder = '' } + secondaryAxValueInput.current.className = 'Input Contained' secondaryAxValueInput.current.disabled = true secondaryAxValueInput.current.placeholder = '' } @@ -282,29 +402,26 @@ const AXSelect = (props: Props) => {
- + +

{errors.axValue1}

@@ -312,29 +429,26 @@ const AXSelect = (props: Props) => {
- +

{errors.axValue2}

diff --git a/components/Button/index.scss b/components/Button/index.scss index ec6dbbf5..60aea5b9 100644 --- a/components/Button/index.scss +++ b/components/Button/index.scss @@ -60,6 +60,17 @@ } } + &:disabled { + background-color: var(--button-bg-disabled); + color: var(--button-text-disabled); + + &:hover { + background-color: var(--button-bg-disabled); + color: var(--button-text-disabled); + cursor: default; + } + } + &.medium { height: $unit * 5.5; padding: ($unit * 1.5) $unit-2x; diff --git a/components/ElementToggle/index.scss b/components/ElementToggle/index.scss index 66b60813..6ce7af42 100644 --- a/components/ElementToggle/index.scss +++ b/components/ElementToggle/index.scss @@ -1,7 +1,8 @@ .ToggleGroup { $height: 36px; - border: 1px solid rgba(0, 0, 0, 0.14); + background-color: var(--toggle-bg); + border: 1px solid var(--toggle-stroke); border-radius: $height; display: flex; height: $height; @@ -9,10 +10,10 @@ padding: calc($unit / 2); .ToggleItem { - background: $grey-100; + background: var(--toggle-bg); border: none; border-radius: 18px; - color: $grey-50; + color: var(--input-secondary); flex-grow: 1; font-size: $font-regular; padding: ($unit) $unit * 2; @@ -32,33 +33,33 @@ color: $grey-15; &.fire { - background: $fire-bg-20; - color: $fire-text-10; + background: var(--fire-bg); + color: var(--fire-text); } &.water { - background: $water-bg-20; - color: $water-text-10; + background: var(--water-bg); + color: var(--water-text); } &.earth { - background: $earth-bg-20; - color: $earth-text-10; + background: var(--earth-bg); + color: var(--earth-text); } &.wind { - background: $wind-bg-20; - color: $wind-text-10; + background: var(--wind-bg); + color: var(--wind-text); } &.dark { - background: $dark-bg-10; - color: $dark-text-10; + background: var(--dark-bg); + color: var(--dark-text); } &.light { - background: $light-bg-20; - color: $light-text-10; + background: var(--light-bg); + color: var(--light-text); } } } diff --git a/components/Input/index.scss b/components/Input/index.scss index 9ec5d332..a55cef8b 100644 --- a/components/Input/index.scss +++ b/components/Input/index.scss @@ -5,7 +5,7 @@ border-radius: 6px; box-sizing: border-box; display: block; - padding: ($unit * 1.5) $unit-2x; + padding: $unit-2x; width: 100%; &.Bound { @@ -15,6 +15,10 @@ background-color: var(--input-bound-bg-hover); } } + + &.Hidden { + display: none; + } } .InputError { diff --git a/components/Input/index.tsx b/components/Input/index.tsx index 23beb955..054e1aa1 100644 --- a/components/Input/index.tsx +++ b/components/Input/index.tsx @@ -1,5 +1,6 @@ +import React, { useEffect, useState } from 'react' import classNames from 'classnames' -import React from 'react' + import './index.scss' interface Props @@ -11,12 +12,26 @@ interface Props label?: string } -const Input = React.forwardRef(function input( +const Input = React.forwardRef(function Input( props: Props, forwardedRef ) { + // States + const [inputValue, setInputValue] = useState('') + + // Classes const classes = classNames({ Input: true }, props.className) - const { value, ...inputProps } = props + const { defaultValue, ...inputProps } = props + + // Change value when prop updates + useEffect(() => { + if (props.value) setInputValue(`${props.value}`) + }, [props.value]) + + function handleChange(event: React.ChangeEvent) { + setInputValue(event.target.value) + if (props.onChange) props.onChange(event) + } return (
diff --git a/components/WeaponUnit/index.scss b/components/WeaponUnit/index.scss index 2b725eb1..225b005d 100644 --- a/components/WeaponUnit/index.scss +++ b/components/WeaponUnit/index.scss @@ -41,6 +41,23 @@ width: 200px; height: auto; + .Awakening { + width: 40%; + height: auto; + top: 67%; + left: -3.5%; + } + + .Modifiers > .Skills { + bottom: 12%; + right: -3.5%; + + & > .Skill { + width: 25%; + height: auto; + } + } + @media (max-width: $medium-screen) { width: 25vw; } @@ -56,6 +73,23 @@ width: 160px; height: auto; + .Awakening { + width: 30%; + height: auto; + top: 14%; + left: -3.5%; + } + + .Modifiers > .Skills { + bottom: 12%; + left: -3.5%; + + & > .Skill { + width: 20%; + height: auto; + } + } + @media (max-width: $medium-screen) { width: 20vw; } @@ -81,7 +115,7 @@ position: absolute; left: $unit; top: $unit; - z-index: 3; + z-index: 10; } h3 { @@ -102,12 +136,37 @@ justify-content: center; margin-bottom: calc($unit / 4); overflow: hidden; + position: relative; transition: all 0.18s ease-in-out; &:hover .icon svg { fill: var(--icon-secondary-hover); } + .Awakening { + width: 100%; + height: 100%; + position: absolute; + z-index: 3; + } + + .Modifiers { + display: flex; + align-items: flex-end; + width: 100%; + height: 100%; + position: absolute; + z-index: 3; + + .Skills { + display: flex; + gap: $unit-fourth; + justify-content: flex-end; + padding: $unit-half; + width: 100%; + } + } + img { position: relative; width: 100%; diff --git a/components/WeaponUnit/index.tsx b/components/WeaponUnit/index.tsx index 6de29e14..bc1c40ba 100644 --- a/components/WeaponUnit/index.tsx +++ b/components/WeaponUnit/index.tsx @@ -9,9 +9,12 @@ import WeaponHovercard from '~components/WeaponHovercard' import UncapIndicator from '~components/UncapIndicator' import Button from '~components/Button' -import { ButtonType } from '~utils/enums' import type { SearchableObject } from '~types' +import { appState } from '~utils/appState' +import { axData } from '~utils/axData' +import { weaponAwakening } from '~utils/awakening' + import PlusIcon from '~public/icons/Add.svg' import SettingsIcon from '~public/icons/Settings.svg' import './index.scss' @@ -71,6 +74,265 @@ const WeaponUnit = (props: Props) => { setImageUrl(imgSrc) } + function awakeningImage() { + if ( + props.gridWeapon && + props.gridWeapon.object.awakening && + props.gridWeapon.awakening && + props.gridWeapon.awakening.type >= 0 + ) { + const awakening = weaponAwakening.find( + (awakening) => awakening.id === props.gridWeapon?.awakening?.type + ) + const name = awakening?.name[locale] + + return ( + {`${name} + ) + } + } + + function telumaImage(index: number) { + const baseUrl = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-keys/` + let filename = '' + let altText = '' + + // If there is a grid weapon, it is a Draconic Weapon and it has keys + if ( + props.gridWeapon && + props.gridWeapon.object.series === 3 && + props.gridWeapon.weapon_keys + ) { + if (index === 0 && props.gridWeapon.weapon_keys[0]) { + altText = `${props.gridWeapon.weapon_keys[0].name[locale]}` + filename = `${props.gridWeapon.weapon_keys[0].slug}.png` + } else if (index === 1 && props.gridWeapon.weapon_keys[1]) { + altText = `${props.gridWeapon.weapon_keys[1].name[locale]}` + + const element = props.gridWeapon.object.element + filename = `${props.gridWeapon.weapon_keys[1].slug}-${element}.png` + } + + return ( + {altText} + ) + } + } + + function telumaImages() { + let images: JSX.Element[] = [] + if ( + props.gridWeapon && + props.gridWeapon.object.series === 3 && + props.gridWeapon.weapon_keys && + props.gridWeapon.weapon_keys.length > 0 + ) { + for (let i = 0; i < props.gridWeapon.weapon_keys.length; i++) { + const image = telumaImage(i) + if (image) images.push(image) + } + } + + return images + } + + function ultimaImage(index: number) { + const baseUrl = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-keys/` + let filename = '' + let altText = '' + + // If there is a grid weapon, it is a Dark Opus Weapon and it has keys + if ( + props.gridWeapon && + props.gridWeapon.object.series === 17 && + props.gridWeapon.weapon_keys + ) { + if ( + props.gridWeapon.weapon_keys[index] && + (props.gridWeapon.weapon_keys[index].slot === 1 || + props.gridWeapon.weapon_keys[index].slot === 2) + ) { + altText = `${props.gridWeapon.weapon_keys[index].name[locale]}` + filename = `${props.gridWeapon.weapon_keys[index].slug}.png` + } else if ( + props.gridWeapon.weapon_keys[index] && + props.gridWeapon.weapon_keys[index].slot === 0 + ) { + altText = `${props.gridWeapon.weapon_keys[index].name[locale]}` + + const weapon = props.gridWeapon.object.proficiency + + const suffix = `${weapon}` + filename = `${props.gridWeapon.weapon_keys[index].slug}-${suffix}.png` + } + } + + return ( + {`${altText}`} + ) + } + + function ultimaImages() { + let images: JSX.Element[] = [] + if ( + props.gridWeapon && + props.gridWeapon.object.series === 17 && + props.gridWeapon.weapon_keys && + props.gridWeapon.weapon_keys.length > 0 + ) { + for (let i = 0; i < props.gridWeapon.weapon_keys.length; i++) { + const image = ultimaImage(i) + if (image) images.push(image) + } + } + + return images + } + + function opusImage(index: number) { + const baseUrl = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-keys/` + let filename = '' + let altText = '' + + // If there is a grid weapon, it is a Dark Opus Weapon and it has keys + if ( + props.gridWeapon && + props.gridWeapon.object.series === 2 && + props.gridWeapon.weapon_keys + ) { + if ( + props.gridWeapon.weapon_keys[index] && + props.gridWeapon.weapon_keys[index].slot === 0 + ) { + altText = `${props.gridWeapon.weapon_keys[index].name[locale]}` + filename = `${props.gridWeapon.weapon_keys[index].slug}.png` + } else if ( + props.gridWeapon.weapon_keys[index] && + props.gridWeapon.weapon_keys[index].slot === 1 + ) { + altText = `${props.gridWeapon.weapon_keys[index].name[locale]}` + + const element = props.gridWeapon.object.element + const mod = props.gridWeapon.object.name.en.includes('Repudiation') + ? 'primal' + : 'magna' + + const suffix = `${mod}-${element}` + const weaponKey = props.gridWeapon.weapon_keys[index] + + if ( + [ + 'pendulum-strength', + 'pendulum-zeal', + 'pendulum-strife', + 'chain-temperament', + 'chain-restoration', + 'chain-glorification', + ].includes(weaponKey.slug) + ) { + filename = `${props.gridWeapon.weapon_keys[index].slug}-${suffix}.png` + } else { + filename = `${props.gridWeapon.weapon_keys[index].slug}.png` + } + } + + return ( + {altText} + ) + } + } + + function opusImages() { + let images: JSX.Element[] = [] + if ( + props.gridWeapon && + props.gridWeapon.object.series === 2 && + props.gridWeapon.weapon_keys && + props.gridWeapon.weapon_keys.length > 0 + ) { + for (let i = 0; i < props.gridWeapon.weapon_keys.length; i++) { + const image = opusImage(i) + if (image) images.push(image) + } + } + + return images + } + + function axImage(index: number) { + const axSkill = getCanonicalAxSkill(index) + + if ( + props.gridWeapon && + props.gridWeapon.object.ax && + props.gridWeapon.object.ax > 0 && + props.gridWeapon.ax && + axSkill + ) { + return ( + {`axskill`} + ) + } + } + + function axImages() { + let images: JSX.Element[] = [] + if ( + props.gridWeapon && + props.gridWeapon.object.ax > 0 && + props.gridWeapon.ax && + props.gridWeapon.ax.length > 0 + ) { + for (let i = 0; i < props.gridWeapon.ax.length; i++) { + const image = axImage(i) + if (image) images.push(image) + } + } + + return images + } + + function getCanonicalAxSkill(index: number) { + if ( + props.gridWeapon && + props.gridWeapon.object.ax && + props.gridWeapon.object.ax > 0 && + props.gridWeapon.ax + ) { + const axOptions = axData[props.gridWeapon.object.ax - 1] + const weaponAxSkill: SimpleAxSkill = props.gridWeapon.ax[0] + + let axSkill = axOptions.find((ax) => ax.id === weaponAxSkill.modifier) + + if (index !== 0 && axSkill && axSkill.secondary) { + const weaponSubAxSkill: SimpleAxSkill = props.gridWeapon.ax[1] + axSkill = axSkill.secondary.find( + (ax) => ax.id === weaponSubAxSkill.modifier + ) + } + + return axSkill + } else return + } + function passUncapData(uncap: number) { if (props.gridWeapon) props.updateUncap(props.gridWeapon.id, props.position, uncap) @@ -81,12 +343,22 @@ const WeaponUnit = (props: Props) => { return ( weapon.ax > 0 || + weapon.awakening || (weapon.series && [2, 3, 17, 22, 24].includes(weapon.series)) ) } const image = (
+
+ {awakeningImage()} +
+ {axImages()} + {telumaImages()} + {opusImages()} + {ultimaImages()} +
+
{weapon?.name.en} {props.editable ? ( diff --git a/pages/new/index.tsx b/pages/new/index.tsx index d1c48f0e..0fe8bb7e 100644 --- a/pages/new/index.tsx +++ b/pages/new/index.tsx @@ -4,17 +4,20 @@ import { serverSideTranslations } from 'next-i18next/serverSideTranslations' import Party from '~components/Party' import { appState } from '~utils/appState' +import { groupWeaponKeys } from '~utils/groupWeaponKeys' import organizeRaids from '~utils/organizeRaids' import setUserToken from '~utils/setUserToken' import api from '~utils/api' import type { NextApiRequest, NextApiResponse } from 'next' +import type { GroupedWeaponKeys } from '~utils/groupWeaponKeys' interface Props { jobs: Job[] jobSkills: JobSkill[] raids: Raid[] sortedRaids: Raid[][] + weaponKeys: GroupedWeaponKeys } const NewRoute: React.FC = (props: Props) => { @@ -31,6 +34,7 @@ const NewRoute: React.FC = (props: Props) => { appState.raids = props.raids appState.jobs = props.jobs appState.jobSkills = props.jobSkills + appState.weaponKeys = props.weaponKeys } return @@ -61,9 +65,11 @@ export const getServerSideProps = async ({ req, res, locale, query }: { req: Nex return response.data }) - let jobSkills = await api.allJobSkills().then((response) => { - return response.data - }) + let jobSkills = await api.allJobSkills().then((response) => response.data) + + let weaponKeys = await api.endpoints.weapon_keys + .getAll() + .then((response) => groupWeaponKeys(response.data)) return { props: { @@ -71,6 +77,7 @@ export const getServerSideProps = async ({ req, res, locale, query }: { req: Nex jobSkills: jobSkills, raids: raids, sortedRaids: sortedRaids, + weaponKeys: weaponKeys, ...(await serverSideTranslations(locale, ['common'])), // Will be passed to the page component as props }, diff --git a/pages/p/[party].tsx b/pages/p/[party].tsx index 0fca65ab..0496d603 100644 --- a/pages/p/[party].tsx +++ b/pages/p/[party].tsx @@ -5,10 +5,12 @@ import { serverSideTranslations } from 'next-i18next/serverSideTranslations' import Party from '~components/Party' import { appState } from '~utils/appState' +import { groupWeaponKeys } from '~utils/groupWeaponKeys' import organizeRaids from '~utils/organizeRaids' import api from '~utils/api' import type { NextApiRequest, NextApiResponse } from 'next' +import type { GroupedWeaponKeys } from '~utils/groupWeaponKeys' interface Props { party: Party @@ -16,6 +18,7 @@ interface Props { jobSkills: JobSkill[] raids: Raid[] sortedRaids: Raid[][] + weaponKeys: GroupedWeaponKeys } const PartyRoute: React.FC = (props: Props) => { @@ -27,6 +30,7 @@ const PartyRoute: React.FC = (props: Props) => { appState.raids = props.raids appState.jobs = props.jobs appState.jobSkills = props.jobSkills + appState.weaponKeys = props.weaponKeys } return @@ -65,6 +69,10 @@ export const getServerSideProps = async ({ req, res, locale, query }: { req: Nex }) let jobSkills = await api.allJobSkills(headers).then((response) => response.data) + + let weaponKeys = await api.endpoints.weapon_keys + .getAll() + .then((response) => groupWeaponKeys(response.data)) let party: Party | null = null if (query.party) { @@ -81,6 +89,7 @@ export const getServerSideProps = async ({ req, res, locale, query }: { req: Nex jobSkills: jobSkills, raids: raids, sortedRaids: sortedRaids, + weaponKeys: weaponKeys, ...(await serverSideTranslations(locale, ["common"])), // Will be passed to the page component as props }, diff --git a/public/icons/Settings.svg b/public/icons/Settings.svg index 9e8741c4..b06f6f3c 100644 --- a/public/icons/Settings.svg +++ b/public/icons/Settings.svg @@ -1,6 +1,3 @@ - - - - - + + diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 63af7fdc..2de21696 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -8,6 +8,15 @@ "value_empty": "{{name}} must have a value" } }, + "awakening": { + "no_type": "No awakening", + "errors": { + "value_too_low": "Awakening level must be at least {{minValue}}", + "value_too_high": "Awakening level cannot be greater than {{maxValue}}", + "value_not_whole": "Awakening level must be a whole number", + "value_empty": "Awakening must have a level" + } + }, "buttons": { "cancel": "Cancel", "copy": "Copy link", @@ -192,6 +201,7 @@ "subtitles": { "element": "Element", "ax_skills": "AX Skills", + "awakening": "Awakening", "weapon_keys": "Weapon Keys" } } diff --git a/public/locales/ja/common.json b/public/locales/ja/common.json index d49f722c..7dc42e3f 100644 --- a/public/locales/ja/common.json +++ b/public/locales/ja/common.json @@ -3,11 +3,20 @@ "no_skill": "EXスキルなし", "errors": { "value_too_low": "{{name}}は最低{{minValue}}{{suffix}}を入力してください", - "value_too_high": "{{name}}は最大{{maxValue}}を入力してください", + "value_too_high": "{{name}}は最大{{maxValue}}{{suffix}}を入力してください", "value_not_whole": "{{name}}は整数でなければなりません", "value_empty": "{{name}}を入力してください" } }, + "awakening": { + "no_type": "覚醒タイプなし", + "errors": { + "value_too_low": "覚醒レベルは最低{{minValue}}を入力してください", + "value_too_high": "覚醒レベルは最大{{maxValue}}を入力してください", + "value_not_whole": "覚醒レベルは整数でなければなりません", + "value_empty": "覚醒レベルを入力してください" + } + }, "buttons": { "cancel": "キャンセルs", "copy": "リンクをコピー", @@ -193,6 +202,7 @@ "subtitles": { "element": "属性", "ax_skills": "EXスキル", + "awakening": "覚醒", "weapon_keys": "武器スキル" } } diff --git a/styles/themes.scss b/styles/themes.scss index 37b5b400..4871bb75 100644 --- a/styles/themes.scss +++ b/styles/themes.scss @@ -38,6 +38,8 @@ --select-bg: #{$select--bg--light}; --select-contained-bg: #{$select--contained--bg--light}; --select-contained-bg-hover: #{$select--contained--bg--light--hover}; + --select-modal-bg: #{$select--modal--bg--light}; + --select-modal-bg-hover: #{$select--modal--bg--light--hover}; --select-separator: #{$select--separator--light}; --option-bg-hover: #{$option--bg--light--hover}; @@ -72,19 +74,40 @@ --subaura-orange-secondary: #{$subaura--orange--secondary--light}; --subaura-orange-text: #{$subaura--orange--text--light}; + // Light - Element Toggle + --toggle-bg: #{$toggle--bg--light}; + --toggle-stroke: #{$toggle--stroke--light}; + --grid-border-color: #{$grid--border--color--light}; + --wind-bg: #{$wind-bg-10}; --wind-hover-bg: #{$wind-bg-20}; + --wind-text: #{$wind-text-10}; --wind-hover-text: #{$wind-text-20}; + + --fire-bg: #{$fire-bg-10}; --fire-hover-bg: #{$fire-bg-20}; + --fire-text: #{$fire-text-10}; --fire-hover-text: #{$fire-text-20}; + + --water-bg: #{$water-bg-10}; --water-hover-bg: #{$water-bg-20}; + --water-text: #{$water-text-10}; --water-hover-text: #{$water-text-20}; + + --earth-bg: #{$earth-bg-10}; --earth-hover-bg: #{$earth-bg-20}; + --earth-text: #{$earth-text-10}; --earth-hover-text: #{$earth-text-20}; + + --dark-bg: #{$dark-bg-10}; --dark-hover-bg: #{$dark-bg-20}; + --dark-text: #{$dark-text-10}; --dark-hover-text: #{$dark-text-20}; + + --light-bg: #{$light-bg-10}; --light-hover-bg: #{$light-bg-20}; + --light-text: #{$light-text-10}; --light-hover-text: #{$light-text-20}; } @@ -128,6 +151,8 @@ --select-bg: #{$select--bg--dark}; --select-contained-bg: #{$select--contained--bg--dark}; --select-contained-bg-hover: #{$select--contained--bg--dark--hover}; + --select-modal-bg: #{$select--modal--bg--dark}; + --select-modal-bg-hover: #{$select--modal--bg--dark--hover}; --select-separator: #{$select--separator--dark}; --option-bg-hover: #{$option--bg--dark--hover}; @@ -162,16 +187,40 @@ --subaura-orange-secondary: #{$subaura--orange--secondary--dark}; --subaura-orange-text: #{$subaura--orange--text--dark}; + // Dark - Element Toggle + --toggle-bg: #{$toggle--bg--dark}; + --toggle-stroke: #{$toggle--stroke--dark}; + --grid-border-color: #{$grid--border--color--dark}; + // Element theming + --wind-bg: #{$wind-bg-10}; --wind-hover-bg: #{$wind-bg-00}; + --wind-text: #{$wind-text-10}; --wind-hover-text: #{$wind-text-00}; + + --fire-bg: #{$fire-bg-10}; --fire-hover-bg: #{$fire-bg-00}; + --fire-text: #{$fire-text-10}; --fire-hover-text: #{$fire-text-00}; + + --water-bg: #{$water-bg-10}; + --water-hover-bg: #{$water-bg-00}; + --water-text: #{$water-text-10}; + --water-hover-text: #{$water-text-00}; + + --earth-bg: #{$earth-bg-10}; --earth-hover-bg: #{$earth-bg-00}; + --earth-text: #{$earth-text-10}; --earth-hover-text: #{$earth-text-00}; + + --dark-bg: #{$dark-bg-10}; --dark-hover-bg: #{$dark-bg-00}; + --dark-text: #{$dark-text-10}; --dark-hover-text: #{$dark-text-00}; + + --light-bg: #{$light-bg-10}; --light-hover-bg: #{$light-bg-00}; + --light-text: #{$light-text-10}; --light-hover-text: #{$light-text-00}; } diff --git a/styles/variables.scss b/styles/variables.scss index 987c33e2..1a3a53a8 100644 --- a/styles/variables.scss +++ b/styles/variables.scss @@ -19,6 +19,7 @@ $unit-6x: $unit * 6; $unit-8x: $unit * 8; $unit-10x: $unit * 10; $unit-12x: $unit * 12; +$unit-14x: $unit * 14; $unit-20x: $unit * 20; // Colors @@ -34,6 +35,7 @@ $grey-55: #888; $grey-60: #a9a9a9; $grey-70: #c6c6c6; $grey-80: #e9e9e9; +$grey-85: #efefef; $grey-90: #f5f5f5; $grey-95: #fafafa; $grey-100: white; @@ -132,8 +134,8 @@ $menu--text--dark--hover: $grey-15; // Color Definitions: Button $button--bg--light: $grey-80; $button--bg--light--hover: $grey-100; -$button--bg--light--disabled: $grey-50; -$button--contained--bg--light: $grey-90; +$button--bg--light--disabled: $grey-80; +$button--contained--bg--light: $grey-85; $button--contained--bg--light--hover: $grey-80; $button--bg--dark: $grey-00; @@ -144,10 +146,10 @@ $button--contained--bg--dark--hover: $grey-05; $button--text--light: $grey-55; $button--text--light--hover: $grey-40; -$button--text--light--disabled: $grey-40; +$button--text--light--disabled: $grey-70; $button--text--dark: $grey-70; $button--text--dark--hover: $grey-100; -$button--text--dark--disabled: $grey-50; +$button--text--dark--disabled: $grey-40; // Color Definitions: Input $input--bg--light: $grey-100; @@ -155,7 +157,7 @@ $input--bg--light--hover: $grey-95; $input--bg--dark: $grey-40; $input--bg--dark--hover: $grey-30; -$input--bound--bg--light: $grey-90; +$input--bound--bg--light: $grey-85; $input--bound--bg--light--hover: $grey-80; $input--bound--bg--dark: $grey-15; $input--bound--bg--dark--hover: $grey-25; @@ -163,12 +165,20 @@ $input--bound--bg--dark--hover: $grey-25; // Color Definitions: Select $select--bg--light: $grey-100; $select--bg--dark: $grey-10; + $select--contained--bg--light: $grey-90; -$select--contained--bg--dark: $grey-05; $select--contained--bg--light--hover: $grey-80; +$select--contained--bg--dark: $grey-05; $select--contained--bg--dark--hover: $grey-00; + +$select--modal--bg--light: $grey-85; +$select--modal--bg--light--hover: $grey-80; +$select--modal--bg--dark: $grey-40; +$select--modal--bg--dark--hover: $grey-30; + $select--separator--light: $grey-90; $select--separator--dark: $grey-05; + $option--bg--light--hover: $grey-90; $option--bg--dark--hover: $grey-00; @@ -205,6 +215,13 @@ $subaura--orange--primary--dark: $orange-00; $subaura--orange--secondary--dark: $orange-10; $subaura--orange--text--dark: $orange-00; +// Color Definitions: Element Toggle +$toggle--bg--light: $grey-90; +$toggle--bg--dark: $grey-15; + +$toggle--stroke--light: rgba(0, 0, 0, 0.14); +$toggle--stroke--dark: rgba(0, 0, 0, 0.8); + // Color Definitions: Text $text--primary--color--light: $grey-40; $text--primary--color--dark: $grey-90; diff --git a/types/AxSkill.d.ts b/types/AxSkill.d.ts index a8765194..3294cd47 100644 --- a/types/AxSkill.d.ts +++ b/types/AxSkill.d.ts @@ -5,6 +5,7 @@ interface AxSkill { ja: string } id: number + slug: string minValue: number maxValue: number suffix?: string diff --git a/types/GridCharacter.d.ts b/types/GridCharacter.d.ts index 592de829..8c610406 100644 --- a/types/GridCharacter.d.ts +++ b/types/GridCharacter.d.ts @@ -3,4 +3,8 @@ interface GridCharacter { position: number object: Character uncap_level: number + awakening: { + type: number + level: number + } } diff --git a/types/GridWeapon.d.ts b/types/GridWeapon.d.ts index 70592fde..43fd714e 100644 --- a/types/GridWeapon.d.ts +++ b/types/GridWeapon.d.ts @@ -7,4 +7,8 @@ interface GridWeapon { element: number weapon_keys?: Array ax?: Array + awakening?: { + type: number + level: number + } } diff --git a/types/Weapon.d.ts b/types/Weapon.d.ts index f2faeb43..36d95c5d 100644 --- a/types/Weapon.d.ts +++ b/types/Weapon.d.ts @@ -9,6 +9,7 @@ interface Weapon { max_skill_level: number series: number ax: number + awakening: boolean name: { [key: string]: string en: string diff --git a/types/WeaponKey.d.ts b/types/WeaponKey.d.ts index 99dacced..95c45ea9 100644 --- a/types/WeaponKey.d.ts +++ b/types/WeaponKey.d.ts @@ -5,6 +5,7 @@ interface WeaponKey { en: string ja: string } + slug: string series: integer slot: integer group: integer diff --git a/utils/appState.tsx b/utils/appState.tsx index 1782aa17..58ffaf70 100644 --- a/utils/appState.tsx +++ b/utils/appState.tsx @@ -1,5 +1,6 @@ import { proxy } from 'valtio' import { JobSkillObject } from '~types' +import { GroupedWeaponKeys } from './groupWeaponKeys' const emptyJob: Job = { id: '-1', @@ -57,6 +58,7 @@ interface AppState { raids: Raid[] jobs: Job[] jobSkills: JobSkill[] + weaponKeys: GroupedWeaponKeys } export const initialAppState: AppState = { @@ -103,6 +105,13 @@ export const initialAppState: AppState = { raids: [], jobs: [], jobSkills: [], + weaponKeys: { + pendulum: [], + chain: [], + teluma: [], + gauph: [], + emblem: [], + }, } export const appState = proxy(initialAppState) diff --git a/utils/awakening.tsx b/utils/awakening.tsx new file mode 100644 index 00000000..05bfb761 --- /dev/null +++ b/utils/awakening.tsx @@ -0,0 +1,62 @@ +export type Awakening = { + id: number + name: { + [key: string]: string + en: string + ja: string + } +} +export const characterAwakening: Awakening[] = [ + { + id: 0, + name: { + en: 'Balanced', + ja: 'バランス', + }, + }, + { + id: 1, + name: { + en: 'Attack', + ja: '攻撃', + }, + }, + { + id: 2, + name: { + en: 'Defense', + ja: '防御', + }, + }, + { + id: 3, + name: { + en: 'Multiattack', + ja: '連続攻撃', + }, + }, +] + +export const weaponAwakening: Awakening[] = [ + { + id: 0, + name: { + en: 'Attack', + ja: '攻撃', + }, + }, + { + id: 1, + name: { + en: 'Defense', + ja: '防御', + }, + }, + { + id: 2, + name: { + en: 'Special', + ja: '特殊', + }, + }, +] diff --git a/utils/axData.tsx b/utils/axData.tsx index 971dc9ea..5feb0dec 100644 --- a/utils/axData.tsx +++ b/utils/axData.tsx @@ -6,6 +6,7 @@ export const axData: AxSkill[][] = [ ja: '攻撃', }, id: 0, + slug: 'atk', minValue: 1, maxValue: 3.5, suffix: '%', @@ -16,6 +17,7 @@ export const axData: AxSkill[][] = [ ja: '奥義ダメ', }, id: 3, + slug: 'ca-dmg', minValue: 2, maxValue: 4, suffix: '%', @@ -26,6 +28,7 @@ export const axData: AxSkill[][] = [ ja: 'DA確率', }, id: 5, + slug: 'da', minValue: 1, maxValue: 2, suffix: '%', @@ -36,6 +39,7 @@ export const axData: AxSkill[][] = [ ja: 'TA確率', }, id: 6, + slug: 'ta', minValue: 1, maxValue: 2, suffix: '%', @@ -46,6 +50,7 @@ export const axData: AxSkill[][] = [ ja: 'アビ上限', }, id: 7, + slug: 'skill-cap', minValue: 1, maxValue: 2, suffix: '%', @@ -58,6 +63,7 @@ export const axData: AxSkill[][] = [ ja: '防御', }, id: 1, + slug: 'def', minValue: 1, maxValue: 8, suffix: '%', @@ -68,6 +74,7 @@ export const axData: AxSkill[][] = [ ja: 'HP', }, id: 2, + slug: 'hp', minValue: 1, maxValue: 3, suffix: '%', @@ -78,6 +85,7 @@ export const axData: AxSkill[][] = [ ja: '弱体耐性', }, id: 9, + slug: 'debuff', minValue: 1, maxValue: 3, suffix: '%', @@ -88,6 +96,7 @@ export const axData: AxSkill[][] = [ ja: '回復性能', }, id: 10, + slug: 'healing', minValue: 2, maxValue: 5, suffix: '%', @@ -98,6 +107,7 @@ export const axData: AxSkill[][] = [ ja: '背水', }, id: 11, + slug: 'enmity', minValue: 1, maxValue: 3, }, @@ -109,6 +119,7 @@ export const axData: AxSkill[][] = [ ja: 'HP', }, id: 2, + slug: 'hp', minValue: 1, maxValue: 11, suffix: '%', @@ -119,6 +130,7 @@ export const axData: AxSkill[][] = [ ja: '防御', }, id: 1, + slug: 'def', minValue: 1, maxValue: 3, suffix: '%', @@ -129,6 +141,7 @@ export const axData: AxSkill[][] = [ ja: '弱体耐性', }, id: 9, + slug: 'debuff', minValue: 1, maxValue: 3, suffix: '%', @@ -139,6 +152,7 @@ export const axData: AxSkill[][] = [ ja: '回復性能', }, id: 10, + slug: 'healing', minValue: 2, maxValue: 5, suffix: '%', @@ -149,6 +163,7 @@ export const axData: AxSkill[][] = [ ja: '渾身', }, id: 12, + slug: 'stamina', minValue: 1, maxValue: 3, }, @@ -160,6 +175,7 @@ export const axData: AxSkill[][] = [ ja: '奥義ダメ', }, id: 3, + slug: 'ca-dmg', minValue: 2, maxValue: 8.5, suffix: '%', @@ -170,6 +186,7 @@ export const axData: AxSkill[][] = [ ja: '攻撃', }, id: 0, + slug: 'atk', minValue: 1, maxValue: 1.5, suffix: '%', @@ -180,6 +197,7 @@ export const axData: AxSkill[][] = [ ja: '全属性攻撃力', }, id: 13, + slug: 'ele-atk', minValue: 1, maxValue: 5, suffix: '%', @@ -190,6 +208,7 @@ export const axData: AxSkill[][] = [ ja: '奥義上限', }, id: 8, + slug: 'ca-cap', minValue: 1, maxValue: 2, suffix: '%', @@ -200,6 +219,7 @@ export const axData: AxSkill[][] = [ ja: '渾身', }, id: 12, + slug: 'stamina', minValue: 1, maxValue: 3, }, @@ -211,6 +231,7 @@ export const axData: AxSkill[][] = [ ja: '連撃率', }, id: 4, + slug: 'ta', minValue: 1, maxValue: 4, suffix: '%', @@ -221,6 +242,7 @@ export const axData: AxSkill[][] = [ ja: '奥義ダメ', }, id: 3, + slug: 'ca-dmg', minValue: 2, maxValue: 4, suffix: '%', @@ -231,6 +253,7 @@ export const axData: AxSkill[][] = [ ja: '全属性攻撃力', }, id: 13, + slug: 'ele-atk', minValue: 1, maxValue: 5, suffix: '%', @@ -241,6 +264,7 @@ export const axData: AxSkill[][] = [ ja: 'DA確率', }, id: 5, + slug: 'da', minValue: 1, maxValue: 2, suffix: '%', @@ -251,6 +275,7 @@ export const axData: AxSkill[][] = [ ja: 'TA確率', }, id: 6, + slug: 'ta', minValue: 1, maxValue: 2, suffix: '%', @@ -265,6 +290,7 @@ export const axData: AxSkill[][] = [ ja: '攻撃', }, id: 0, + slug: 'atk', minValue: 1, maxValue: 3.5, suffix: '%', @@ -275,6 +301,7 @@ export const axData: AxSkill[][] = [ ja: '奥義ダメ', }, id: 3, + slug: 'ca-dmg', minValue: 2, maxValue: 8.5, suffix: '%', @@ -285,6 +312,7 @@ export const axData: AxSkill[][] = [ ja: '連撃確率', }, id: 4, + slug: 'ta', minValue: 1.5, maxValue: 4, suffix: '%', @@ -295,6 +323,7 @@ export const axData: AxSkill[][] = [ ja: '通常ダメ上限', }, id: 14, + slug: 'na-dmg', minValue: 0.5, maxValue: 1.5, suffix: '%', @@ -305,6 +334,7 @@ export const axData: AxSkill[][] = [ ja: 'アビ与ダメ上昇', }, id: 15, + slug: 'skill-supp', minValue: 1, maxValue: 5, }, @@ -316,6 +346,7 @@ export const axData: AxSkill[][] = [ ja: '防御', }, id: 1, + slug: 'def', minValue: 1, maxValue: 8, suffix: '%', @@ -326,6 +357,7 @@ export const axData: AxSkill[][] = [ ja: '属性ダメ軽減', }, id: 17, + slug: 'ele-def', minValue: 1, maxValue: 5, suffix: '%', @@ -336,6 +368,7 @@ export const axData: AxSkill[][] = [ ja: '弱体耐性', }, id: 9, + slug: 'debuff', minValue: 1, maxValue: 3, suffix: '%', @@ -346,6 +379,7 @@ export const axData: AxSkill[][] = [ ja: '回復性能', }, id: 10, + slug: 'healing', minValue: 2, maxValue: 5, suffix: '%', @@ -356,6 +390,7 @@ export const axData: AxSkill[][] = [ ja: '背水', }, id: 11, + slug: 'enmity', minValue: 1, maxValue: 3, }, @@ -367,6 +402,7 @@ export const axData: AxSkill[][] = [ ja: 'HP', }, id: 2, + slug: 'hp', minValue: 1, maxValue: 11, suffix: '%', @@ -377,6 +413,7 @@ export const axData: AxSkill[][] = [ ja: '属性ダメ軽減', }, id: 17, + slug: 'ele-def', minValue: 1, maxValue: 5, suffix: '%', @@ -387,6 +424,7 @@ export const axData: AxSkill[][] = [ ja: '弱体耐性', }, id: 9, + slug: 'debuff', minValue: 1, maxValue: 3, suffix: '%', @@ -397,6 +435,7 @@ export const axData: AxSkill[][] = [ ja: '回復性能', }, id: 10, + slug: 'healing', minValue: 2, maxValue: 5, suffix: '%', @@ -407,6 +446,7 @@ export const axData: AxSkill[][] = [ ja: '渾身', }, id: 12, + slug: 'stamina', minValue: 1, maxValue: 3, }, @@ -418,6 +458,7 @@ export const axData: AxSkill[][] = [ ja: '奥義ダメ', }, id: 3, + slug: 'ca-dmg', minValue: 2, maxValue: 8.5, suffix: '%', @@ -428,6 +469,7 @@ export const axData: AxSkill[][] = [ ja: '連撃率', }, id: 4, + slug: 'ta', minValue: 1.5, maxValue: 4, suffix: '%', @@ -438,6 +480,7 @@ export const axData: AxSkill[][] = [ ja: 'アビ与ダメ上昇', }, id: 15, + slug: 'skill-supp', minValue: 1, maxValue: 5, }, @@ -447,6 +490,7 @@ export const axData: AxSkill[][] = [ ja: '奥義与ダメ上昇', }, id: 16, + slug: 'ca-supp', minValue: 1, maxValue: 5, }, @@ -456,6 +500,7 @@ export const axData: AxSkill[][] = [ ja: '渾身', }, id: 12, + slug: 'stamina', minValue: 1, maxValue: 3, }, @@ -467,6 +512,7 @@ export const axData: AxSkill[][] = [ ja: '連撃率', }, id: 4, + slug: 'ta', minValue: 1, maxValue: 4, suffix: '%', @@ -477,6 +523,7 @@ export const axData: AxSkill[][] = [ ja: '奥義与ダメ上昇', }, id: 16, + slug: 'ca-supp', minValue: 1, maxValue: 5, }, @@ -486,6 +533,7 @@ export const axData: AxSkill[][] = [ ja: '通常ダメ上限', }, id: 14, + slug: 'na-cap', minValue: 0.5, maxValue: 1.5, suffix: '%', @@ -496,6 +544,7 @@ export const axData: AxSkill[][] = [ ja: '渾身', }, id: 12, + slug: 'stamina', minValue: 1, maxValue: 3, }, @@ -505,6 +554,7 @@ export const axData: AxSkill[][] = [ ja: '背水', }, id: 11, + slug: 'enmity', minValue: 1, maxValue: 3, }, @@ -518,6 +568,7 @@ export const axData: AxSkill[][] = [ ja: '攻撃', }, id: 0, + slug: 'atk', minValue: 1, maxValue: 3.5, suffix: '%', @@ -528,6 +579,7 @@ export const axData: AxSkill[][] = [ ja: '奥義ダメ', }, id: 3, + slug: 'ca-dmg', minValue: 2, maxValue: 4, suffix: '%', @@ -538,6 +590,7 @@ export const axData: AxSkill[][] = [ ja: 'DA確率', }, id: 5, + slug: 'da', minValue: 1, maxValue: 2, suffix: '%', @@ -548,6 +601,7 @@ export const axData: AxSkill[][] = [ ja: 'TA確率', }, id: 6, + slug: 'ta', minValue: 1, maxValue: 2, suffix: '%', @@ -558,6 +612,7 @@ export const axData: AxSkill[][] = [ ja: 'アビ上限', }, id: 7, + slug: 'skill-cap', minValue: 1, maxValue: 2, suffix: '%', @@ -570,6 +625,7 @@ export const axData: AxSkill[][] = [ ja: '防御', }, id: 1, + slug: 'def', minValue: 1, maxValue: 8, suffix: '%', @@ -580,6 +636,7 @@ export const axData: AxSkill[][] = [ ja: 'HP', }, id: 2, + slug: 'hp', minValue: 1, maxValue: 3, suffix: '%', @@ -590,6 +647,7 @@ export const axData: AxSkill[][] = [ ja: '弱体耐性', }, id: 9, + slug: 'debuff', minValue: 1, maxValue: 3, suffix: '%', @@ -600,6 +658,7 @@ export const axData: AxSkill[][] = [ ja: '回復性能', }, id: 10, + slug: 'healing', minValue: 2, maxValue: 5, suffix: '%', @@ -610,6 +669,7 @@ export const axData: AxSkill[][] = [ ja: '背水', }, id: 11, + slug: 'enmity', minValue: 1, maxValue: 3, }, @@ -621,6 +681,7 @@ export const axData: AxSkill[][] = [ ja: 'HP', }, id: 2, + slug: 'hp', minValue: 1, maxValue: 11, suffix: '%', @@ -631,6 +692,7 @@ export const axData: AxSkill[][] = [ ja: '防御', }, id: 1, + slug: 'def', minValue: 1, maxValue: 3, suffix: '%', @@ -641,6 +703,7 @@ export const axData: AxSkill[][] = [ ja: '弱体耐性', }, id: 9, + slug: 'debuff', minValue: 1, maxValue: 3, suffix: '%', @@ -651,6 +714,7 @@ export const axData: AxSkill[][] = [ ja: '回復性能', }, id: 10, + slug: 'healing', minValue: 2, maxValue: 5, suffix: '%', @@ -661,6 +725,7 @@ export const axData: AxSkill[][] = [ ja: '渾身', }, id: 12, + slug: 'stamina', minValue: 1, maxValue: 3, }, @@ -672,6 +737,7 @@ export const axData: AxSkill[][] = [ ja: '奥義ダメ', }, id: 3, + slug: 'ca-dmg', minValue: 2, maxValue: 8.5, suffix: '%', @@ -682,6 +748,7 @@ export const axData: AxSkill[][] = [ ja: '攻撃', }, id: 0, + slug: 'atk', minValue: 1, maxValue: 1.5, suffix: '%', @@ -692,6 +759,7 @@ export const axData: AxSkill[][] = [ ja: '全属性攻撃力', }, id: 13, + slug: 'ele-atk', minValue: 1, maxValue: 5, suffix: '%', @@ -702,6 +770,7 @@ export const axData: AxSkill[][] = [ ja: '奥義上限', }, id: 8, + slug: 'ca-dmg', minValue: 1, maxValue: 2, suffix: '%', @@ -712,6 +781,7 @@ export const axData: AxSkill[][] = [ ja: '渾身', }, id: 12, + slug: 'stamina', minValue: 1, maxValue: 3, }, @@ -723,6 +793,7 @@ export const axData: AxSkill[][] = [ ja: '連撃率', }, id: 4, + slug: 'ta', minValue: 1, maxValue: 4, suffix: '%', @@ -733,6 +804,7 @@ export const axData: AxSkill[][] = [ ja: '奥義ダメ', }, id: 3, + slug: 'ca-dmg', minValue: 2, maxValue: 4, suffix: '%', @@ -743,6 +815,7 @@ export const axData: AxSkill[][] = [ ja: '全属性攻撃力', }, id: 13, + slug: 'ele-atk', minValue: 1, maxValue: 5, suffix: '%', @@ -753,6 +826,7 @@ export const axData: AxSkill[][] = [ ja: 'DA確率', }, id: 5, + slug: 'da', minValue: 1, maxValue: 2, suffix: '%', @@ -763,6 +837,7 @@ export const axData: AxSkill[][] = [ ja: 'TA確率', }, id: 6, + slug: 'ta', minValue: 1, maxValue: 2, suffix: '%', @@ -775,6 +850,7 @@ export const axData: AxSkill[][] = [ ja: 'EXP UP', }, id: 18, + slug: 'exp', minValue: 5, maxValue: 10, suffix: '%', @@ -785,6 +861,7 @@ export const axData: AxSkill[][] = [ ja: '獲得ルピ', }, id: 19, + slug: 'rupie', minValue: 10, maxValue: 20, suffix: '%', diff --git a/utils/groupWeaponKeys.tsx b/utils/groupWeaponKeys.tsx new file mode 100644 index 00000000..cb31b824 --- /dev/null +++ b/utils/groupWeaponKeys.tsx @@ -0,0 +1,34 @@ +import { weaponKeyGroups } from './weaponKeyGroups' + +export type GroupedWeaponKeys = { + [key: string]: WeaponKey[] + pendulum: WeaponKey[] + chain: WeaponKey[] + teluma: WeaponKey[] + gauph: WeaponKey[] + emblem: WeaponKey[] +} + +export function groupWeaponKeys(keys: WeaponKey[]) { + console.log(keys) + const numGroups = Math.max.apply( + Math, + keys.map((key) => key.group) + ) + + let groupedKeys: GroupedWeaponKeys = { + pendulum: [], + chain: [], + teluma: [], + gauph: [], + emblem: [], + } + + for (let i = 0; i <= numGroups; i++) { + groupedKeys[weaponKeyGroups[i].slug] = keys.filter((key) => key.group == i) + } + + console.log(groupedKeys) + + return groupedKeys +} diff --git a/utils/weaponKeyGroups.tsx b/utils/weaponKeyGroups.tsx new file mode 100644 index 00000000..dc21f2ea --- /dev/null +++ b/utils/weaponKeyGroups.tsx @@ -0,0 +1,52 @@ +interface WeaponKeyGroup { + id: number + slug: string + name: { + [key: string]: string + en: string + ja: string + } +} + +export const weaponKeyGroups: WeaponKeyGroup[] = [ + { + id: 0, + slug: 'pendulum', + name: { + en: 'Pendulum', + ja: 'ペンデュラム', + }, + }, + { + id: 1, + slug: 'chain', + name: { + en: 'Chain', + ja: 'チェイン', + }, + }, + { + id: 2, + slug: 'teluma', + name: { + en: 'Teluma', + ja: 'テルマ', + }, + }, + { + id: 3, + slug: 'gauph', + name: { + en: 'Gauph Key', + ja: 'ガフスキー', + }, + }, + { + id: 4, + slug: 'emblem', + name: { + en: 'Emblem', + ja: 'エンブレム', + }, + }, +]