Update awakening (#315)
* Add Awakening type and remove old defs We remove the flat list of awakening data, as we will be pulling data from the database * Update types to use new Awakening type * Update WeaponUnit for Grand weapon awakenings * Update object modals We needed to update CharacterModal and WeaponModal to display awakenings from the new data format. However, the component used (`SelectWithInput`) was tied to AX Skills in a way that would take exponentially more time to resolve. Instead, we forked `SelectWithInput` into `AwakeningSelectWithInput` and did our work there. `AwakeningSelect` was found to be redundant, so it was removed. * Update hovercards
This commit is contained in:
parent
f86a199098
commit
0ebd1a6c66
15 changed files with 316 additions and 306 deletions
|
|
@ -16,7 +16,6 @@ import {
|
|||
aetherialMastery,
|
||||
permanentMastery,
|
||||
} from '~data/overMastery'
|
||||
import { characterAwakening } from '~data/awakening'
|
||||
import { ExtendedMastery } from '~types'
|
||||
|
||||
import './index.scss'
|
||||
|
|
@ -27,13 +26,6 @@ interface Props {
|
|||
onTriggerClick: () => void
|
||||
}
|
||||
|
||||
interface KeyNames {
|
||||
[key: string]: {
|
||||
en: string
|
||||
jp: string
|
||||
}
|
||||
}
|
||||
|
||||
const CharacterHovercard = (props: Props) => {
|
||||
const router = useRouter()
|
||||
const { t } = useTranslation('common')
|
||||
|
|
@ -181,27 +173,20 @@ const CharacterHovercard = (props: Props) => {
|
|||
|
||||
const awakeningSection = () => {
|
||||
const gridAwakening = props.gridCharacter.awakening
|
||||
const awakening = characterAwakening.find(
|
||||
(awakening) => awakening.id === gridAwakening?.type
|
||||
)
|
||||
|
||||
if (gridAwakening && awakening) {
|
||||
if (gridAwakening) {
|
||||
return (
|
||||
<section className="Awakening">
|
||||
<h5 className={tintElement}>
|
||||
{t('modals.characters.subtitles.awakening')}
|
||||
</h5>
|
||||
<div>
|
||||
{gridAwakening.type > 1 ? (
|
||||
<img
|
||||
alt={awakening.name[locale]}
|
||||
src={`${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/awakening/character_${gridAwakening.type}.jpg`}
|
||||
/>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
<img
|
||||
alt={gridAwakening.type.name[locale]}
|
||||
src={`${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/awakening/${gridAwakening.type.slug}.jpg`}
|
||||
/>
|
||||
<span>
|
||||
<strong>{`${awakening.name[locale]}`}</strong>
|
||||
<strong>{`${gridAwakening.type.name[locale]}`}</strong>
|
||||
{`Lv${gridAwakening.level}`}
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,13 +1,7 @@
|
|||
// Core dependencies
|
||||
import React, {
|
||||
PropsWithChildren,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useState,
|
||||
} from 'react'
|
||||
import React, { PropsWithChildren, useEffect, useState } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { AxiosResponse } from 'axios'
|
||||
import classNames from 'classnames'
|
||||
|
||||
// UI dependencies
|
||||
|
|
@ -20,14 +14,10 @@ import {
|
|||
import DialogContent from '~components/common/DialogContent'
|
||||
import Button from '~components/common/Button'
|
||||
import SelectWithInput from '~components/common/SelectWithInput'
|
||||
import AwakeningSelect from '~components/mastery/AwakeningSelect'
|
||||
import RingSelect from '~components/mastery/RingSelect'
|
||||
import Switch from '~components/common/Switch'
|
||||
|
||||
// Utilities
|
||||
import api from '~utils/api'
|
||||
import { appState } from '~utils/appState'
|
||||
import { retrieveCookies } from '~utils/retrieveCookies'
|
||||
import elementalizeAetherialMastery from '~utils/elementalizeAetherialMastery'
|
||||
|
||||
// Data
|
||||
|
|
@ -36,6 +26,8 @@ const emptyExtendedMastery: ExtendedMastery = {
|
|||
strength: 0,
|
||||
}
|
||||
|
||||
const MAX_AWAKENING_LEVEL = 9
|
||||
|
||||
// Styles and icons
|
||||
import CrossIcon from '~public/icons/Cross.svg'
|
||||
import './index.scss'
|
||||
|
|
@ -46,6 +38,7 @@ import {
|
|||
ExtendedMastery,
|
||||
GridCharacterObject,
|
||||
} from '~types'
|
||||
import AwakeningSelectWithInput from '~components/mastery/AwakeningSelectWithInput'
|
||||
|
||||
interface Props {
|
||||
gridCharacter: GridCharacter
|
||||
|
|
@ -66,9 +59,6 @@ const CharacterModal = ({
|
|||
router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en'
|
||||
const { t } = useTranslation('common')
|
||||
|
||||
// Cookies
|
||||
const cookies = retrieveCookies()
|
||||
|
||||
// UI state
|
||||
const [open, setOpen] = useState(false)
|
||||
const [formValid, setFormValid] = useState(false)
|
||||
|
|
@ -103,8 +93,8 @@ const CharacterModal = ({
|
|||
const [earring, setEarring] = useState<ExtendedMastery>(emptyExtendedMastery)
|
||||
|
||||
// Character properties: Awakening
|
||||
const [awakeningType, setAwakeningType] = useState(0)
|
||||
const [awakeningLevel, setAwakeningLevel] = useState(0)
|
||||
const [awakening, setAwakening] = useState<Awakening>()
|
||||
const [awakeningLevel, setAwakeningLevel] = useState(1)
|
||||
|
||||
// Character properties: Transcendence
|
||||
const [transcendenceStep, setTranscendenceStep] = useState(0)
|
||||
|
|
@ -118,7 +108,7 @@ const CharacterModal = ({
|
|||
})
|
||||
}
|
||||
|
||||
setAwakeningType(gridCharacter.awakening.type)
|
||||
setAwakening(gridCharacter.awakening.type)
|
||||
setAwakeningLevel(gridCharacter.awakening.level)
|
||||
setPerpetuity(gridCharacter.perpetuity)
|
||||
}, [gridCharacter])
|
||||
|
|
@ -147,15 +137,16 @@ const CharacterModal = ({
|
|||
modifier: earring.modifier,
|
||||
strength: earring.strength,
|
||||
},
|
||||
awakening: {
|
||||
type: awakeningType,
|
||||
level: awakeningLevel,
|
||||
},
|
||||
transcendence_step: transcendenceStep,
|
||||
perpetuity: perpetuity,
|
||||
},
|
||||
}
|
||||
|
||||
if (awakening) {
|
||||
object.character.awakening_id = awakening.id
|
||||
object.character.awakening_level = awakeningLevel
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
|
|
@ -191,8 +182,8 @@ const CharacterModal = ({
|
|||
if (onOpenChange) onOpenChange(false)
|
||||
}
|
||||
|
||||
function receiveAwakeningValues(type: number, level: number) {
|
||||
setAwakeningType(type)
|
||||
function receiveAwakeningValues(id: string, level: number) {
|
||||
setAwakening(gridCharacter.object.awakenings.find((a) => a.id === id))
|
||||
setAwakeningLevel(level)
|
||||
}
|
||||
|
||||
|
|
@ -234,10 +225,16 @@ const CharacterModal = ({
|
|||
return (
|
||||
<section>
|
||||
<h3>{t('modals.characters.subtitles.awakening')}</h3>
|
||||
<AwakeningSelect
|
||||
object="character"
|
||||
type={awakeningType}
|
||||
level={awakeningLevel}
|
||||
<AwakeningSelectWithInput
|
||||
dataSet={gridCharacter.object.awakenings}
|
||||
awakening={gridCharacter.awakening.type}
|
||||
level={gridCharacter.awakening.level}
|
||||
defaultAwakening={
|
||||
gridCharacter.object.awakenings.find(
|
||||
(a) => a.slug === 'character-balanced'
|
||||
)!
|
||||
}
|
||||
maxLevel={MAX_AWAKENING_LEVEL}
|
||||
sendValidity={receiveValidity}
|
||||
sendValues={receiveAwakeningValues}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,97 +0,0 @@
|
|||
import React, { useEffect, useState } from 'react'
|
||||
import cloneDeep from 'lodash.clonedeep'
|
||||
|
||||
import SelectWithInput from '~components/common/SelectWithInput'
|
||||
import { weaponAwakening, characterAwakening } from '~data/awakening'
|
||||
|
||||
import './index.scss'
|
||||
|
||||
interface Props {
|
||||
object: 'character' | 'weapon'
|
||||
type?: number
|
||||
level?: number
|
||||
onOpenChange?: (open: boolean) => void
|
||||
sendValidity: (isValid: boolean) => void
|
||||
sendValues: (type: number, level: number) => void
|
||||
}
|
||||
|
||||
const AwakeningSelect = (props: Props) => {
|
||||
// Data states
|
||||
const [awakeningType, setAwakeningType] = useState(
|
||||
props.object === 'weapon' ? 0 : 1
|
||||
)
|
||||
const [awakeningLevel, setAwakeningLevel] = useState(1)
|
||||
|
||||
// Data
|
||||
const chooseDataset = () => {
|
||||
let list: ItemSkill[] = []
|
||||
|
||||
switch (props.object) {
|
||||
case 'character':
|
||||
list = characterAwakening
|
||||
break
|
||||
case 'weapon':
|
||||
// WARNING: Clonedeep is masking a deeper error
|
||||
// which is running this method every time this component is rerendered
|
||||
// causing multiple "No awakening" items to be added
|
||||
const awakening = cloneDeep(weaponAwakening)
|
||||
awakening.unshift({
|
||||
id: 0,
|
||||
name: {
|
||||
en: 'No awakening',
|
||||
ja: '覚醒なし',
|
||||
},
|
||||
granblue_id: '',
|
||||
slug: 'no-awakening',
|
||||
minValue: 0,
|
||||
maxValue: 0,
|
||||
fractional: false,
|
||||
})
|
||||
list = awakening
|
||||
break
|
||||
}
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
// Set default awakening and level based on object type
|
||||
useEffect(() => {
|
||||
const defaultAwakening = props.object === 'weapon' ? 0 : 1
|
||||
const type = props.type != undefined ? props.type : defaultAwakening
|
||||
|
||||
setAwakeningType(type)
|
||||
setAwakeningLevel(props.level ? props.level : 1)
|
||||
}, [props.object, props.type, props.level])
|
||||
|
||||
// Send validity of form when awakening level changes
|
||||
useEffect(() => {
|
||||
props.sendValidity(awakeningLevel > 0)
|
||||
}, [props.sendValidity, awakeningLevel])
|
||||
|
||||
// Classes
|
||||
function changeOpen(open: boolean) {
|
||||
if (props.onOpenChange) props.onOpenChange(open)
|
||||
}
|
||||
|
||||
function handleValueChange(type: number, level: number) {
|
||||
setAwakeningType(type)
|
||||
setAwakeningLevel(level)
|
||||
props.sendValues(type, level)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="Awakening">
|
||||
<SelectWithInput
|
||||
object={`${props.object}_awakening`}
|
||||
dataSet={chooseDataset()}
|
||||
selectValue={awakeningType}
|
||||
inputValue={awakeningLevel}
|
||||
onOpenChange={changeOpen}
|
||||
sendValidity={props.sendValidity}
|
||||
sendValues={handleValueChange}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AwakeningSelect
|
||||
|
|
@ -1,4 +1,22 @@
|
|||
.AwakeningSelect .AwakeningSet {
|
||||
.SelectWithItem {
|
||||
.InputSet {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: $unit;
|
||||
width: 100%;
|
||||
|
||||
.SelectTrigger {
|
||||
flex-grow: 1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.Input {
|
||||
flex-grow: 0;
|
||||
text-align: right;
|
||||
width: 13rem;
|
||||
}
|
||||
}
|
||||
|
||||
.errors {
|
||||
color: $error;
|
||||
display: none;
|
||||
|
|
@ -8,30 +26,4 @@
|
|||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.fields {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: $unit;
|
||||
width: 100%;
|
||||
|
||||
.SelectTrigger {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.Label {
|
||||
display: none;
|
||||
flex-grow: 0;
|
||||
|
||||
&.Visible {
|
||||
display: block;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.Input {
|
||||
min-width: $unit * 12;
|
||||
width: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
215
components/mastery/AwakeningSelectWithInput/index.tsx
Normal file
215
components/mastery/AwakeningSelectWithInput/index.tsx
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
// Core dependencies
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import classNames from 'classnames'
|
||||
|
||||
// UI Dependencies
|
||||
import Input from '~components/common/Input'
|
||||
import Select from '~components/common/Select'
|
||||
import SelectItem from '~components/common/SelectItem'
|
||||
|
||||
// Styles and icons
|
||||
import './index.scss'
|
||||
|
||||
// Types
|
||||
interface Props {
|
||||
dataSet: Awakening[]
|
||||
defaultAwakening: Awakening
|
||||
awakening?: Awakening
|
||||
level?: number
|
||||
maxLevel: number
|
||||
selectDisabled: boolean
|
||||
onOpenChange?: (open: boolean) => void
|
||||
sendValidity: (isValid: boolean) => void
|
||||
sendValues: (type: string, level: number) => void
|
||||
}
|
||||
|
||||
const defaultProps = {
|
||||
selectDisabled: false,
|
||||
}
|
||||
|
||||
const AwakeningSelectWithInput = ({
|
||||
dataSet,
|
||||
defaultAwakening,
|
||||
awakening,
|
||||
level,
|
||||
maxLevel,
|
||||
selectDisabled,
|
||||
onOpenChange,
|
||||
sendValidity,
|
||||
sendValues,
|
||||
}: Props) => {
|
||||
// Set up translations
|
||||
const router = useRouter()
|
||||
const locale =
|
||||
router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en'
|
||||
const { t } = useTranslation('common')
|
||||
|
||||
// State: Component
|
||||
const [open, setOpen] = useState(false)
|
||||
const [error, setError] = useState('')
|
||||
|
||||
// State: Data
|
||||
const [currentAwakening, setCurrentAwakening] = useState<Awakening>()
|
||||
const [currentLevel, setCurrentLevel] = useState(1)
|
||||
|
||||
// Refs
|
||||
const inputRef = React.createRef<HTMLInputElement>()
|
||||
|
||||
// Classes
|
||||
const inputClasses = classNames({
|
||||
Bound: true,
|
||||
Hidden: currentAwakening === undefined || currentAwakening.id === '0',
|
||||
})
|
||||
|
||||
const errorClasses = classNames({
|
||||
errors: true,
|
||||
visible: error !== '',
|
||||
})
|
||||
|
||||
// Hooks
|
||||
useEffect(() => {
|
||||
setCurrentAwakening(awakening)
|
||||
setCurrentLevel(level ? level : 1)
|
||||
|
||||
if (awakening) sendValidity(true)
|
||||
}, [])
|
||||
|
||||
// Methods: UI state management
|
||||
function changeOpen() {
|
||||
if (!selectDisabled) {
|
||||
setOpen(!open)
|
||||
if (onOpenChange) onOpenChange(!open)
|
||||
}
|
||||
}
|
||||
|
||||
function onClose() {
|
||||
if (onOpenChange) onOpenChange(false)
|
||||
}
|
||||
|
||||
// Methods: Rendering
|
||||
function generateOptions() {
|
||||
const sortedDataSet = [...dataSet].sort((a, b) => {
|
||||
return a.order - b.order
|
||||
})
|
||||
|
||||
let options: React.ReactNode[] = sortedDataSet.map((awakening, i) => {
|
||||
return generateItem(awakening)
|
||||
})
|
||||
|
||||
if (!dataSet.includes(defaultAwakening))
|
||||
options.unshift(generateItem(defaultAwakening))
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
function generateItem(awakening: Awakening) {
|
||||
return (
|
||||
<SelectItem key={awakening.slug} value={awakening.id}>
|
||||
{awakening.name[locale]}
|
||||
</SelectItem>
|
||||
)
|
||||
}
|
||||
|
||||
// Methods: User input detection
|
||||
function handleSelectChange(id: string) {
|
||||
const input = inputRef.current
|
||||
if (input && !handleInputError(parseFloat(input.value))) return
|
||||
|
||||
setCurrentAwakening(dataSet.find((awakening) => awakening.id === id))
|
||||
sendValues(id, currentLevel)
|
||||
}
|
||||
|
||||
function handleInputChange(event: React.ChangeEvent<HTMLInputElement>) {
|
||||
const input = inputRef.current
|
||||
if (input && !handleInputError(parseFloat(input.value))) return
|
||||
|
||||
setCurrentLevel(parseInt(event.target.value))
|
||||
sendValues(
|
||||
currentAwakening ? currentAwakening.id : '0',
|
||||
parseInt(event.target.value)
|
||||
)
|
||||
}
|
||||
|
||||
// Methods: Handle error
|
||||
|
||||
function handleInputError(value: number) {
|
||||
let error = ''
|
||||
|
||||
if (currentAwakening) {
|
||||
if (value < 1) {
|
||||
error = t(`awakening.errors.value_too_low`, {
|
||||
minValue: 1,
|
||||
})
|
||||
} else if (value > maxLevel) {
|
||||
error = t(`awakening.errors.value_too_high`, {
|
||||
maxValue: maxLevel,
|
||||
})
|
||||
} 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)
|
||||
|
||||
if (error.length > 0) {
|
||||
sendValidity(false)
|
||||
return false
|
||||
} else return true
|
||||
}
|
||||
|
||||
const rangeString = () => {
|
||||
let placeholder = ''
|
||||
|
||||
if (awakening) {
|
||||
const minValue = 1
|
||||
const maxValue = maxLevel
|
||||
placeholder = `${minValue}~${maxValue}`
|
||||
}
|
||||
|
||||
return placeholder
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="SelectWithItem">
|
||||
<div className="InputSet">
|
||||
<Select
|
||||
key="awakening-type"
|
||||
value={`${awakening ? awakening.id : defaultAwakening.id}`}
|
||||
open={open}
|
||||
disabled={selectDisabled}
|
||||
onValueChange={handleSelectChange}
|
||||
onOpenChange={changeOpen}
|
||||
onClose={onClose}
|
||||
triggerClass="modal"
|
||||
overlayVisible={false}
|
||||
>
|
||||
{generateOptions()}
|
||||
</Select>
|
||||
|
||||
<Input
|
||||
value={level ? level : 1}
|
||||
className={inputClasses}
|
||||
type="number"
|
||||
placeholder={rangeString()}
|
||||
min={1}
|
||||
max={maxLevel}
|
||||
step="1"
|
||||
onChange={handleInputChange}
|
||||
visible={awakening ? 'true' : 'false'}
|
||||
ref={inputRef}
|
||||
/>
|
||||
</div>
|
||||
<p className={errorClasses}>{error}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
AwakeningSelectWithInput.defaultProps = defaultProps
|
||||
|
||||
export default AwakeningSelectWithInput
|
||||
|
|
@ -12,7 +12,6 @@ import WeaponLabelIcon from '~components/weapon/WeaponLabelIcon'
|
|||
import UncapIndicator from '~components/uncap/UncapIndicator'
|
||||
|
||||
import ax from '~data/ax'
|
||||
import { weaponAwakening } from '~data/awakening'
|
||||
|
||||
import './index.scss'
|
||||
|
||||
|
|
@ -146,11 +145,8 @@ const WeaponHovercard = (props: Props) => {
|
|||
|
||||
const awakeningSection = () => {
|
||||
const gridAwakening = props.gridWeapon.awakening
|
||||
const awakening = weaponAwakening.find(
|
||||
(awakening) => awakening.id === gridAwakening?.type
|
||||
)
|
||||
|
||||
if (gridAwakening && awakening) {
|
||||
if (gridAwakening) {
|
||||
return (
|
||||
<section className="awakening">
|
||||
<h5 className={tintElement}>
|
||||
|
|
@ -158,11 +154,11 @@ const WeaponHovercard = (props: Props) => {
|
|||
</h5>
|
||||
<div>
|
||||
<img
|
||||
alt={awakening.name[locale]}
|
||||
src={`${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/awakening/weapon_${gridAwakening.type}.png`}
|
||||
alt={gridAwakening.type.name[locale]}
|
||||
src={`${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/awakening/${gridAwakening.type.slug}.png`}
|
||||
/>
|
||||
<span>
|
||||
<strong>{`${awakening.name[locale]}`}</strong>
|
||||
<strong>{`${gridAwakening.type.name[locale]}`}</strong>
|
||||
{`Lv${gridAwakening.level}`}
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -11,14 +11,15 @@ import {
|
|||
DialogTrigger,
|
||||
} from '~components/common/Dialog'
|
||||
import DialogContent from '~components/common/DialogContent'
|
||||
import AwakeningSelectWithInput from '~components/mastery/AwakeningSelectWithInput'
|
||||
import AXSelect from '~components/mastery/AxSelect'
|
||||
import AwakeningSelect from '~components/mastery/AwakeningSelect'
|
||||
import ElementToggle from '~components/ElementToggle'
|
||||
import WeaponKeySelect from '~components/weapon/WeaponKeySelect'
|
||||
import Button from '~components/common/Button'
|
||||
|
||||
import api from '~utils/api'
|
||||
import { appState } from '~utils/appState'
|
||||
import { NO_AWAKENING } from '~data/awakening'
|
||||
|
||||
import CrossIcon from '~public/icons/Cross.svg'
|
||||
import './index.scss'
|
||||
|
|
@ -33,7 +34,7 @@ interface GridWeaponObject {
|
|||
ax_modifier2?: number
|
||||
ax_strength1?: number
|
||||
ax_strength2?: number
|
||||
awakening_type?: number
|
||||
awakening_id?: string
|
||||
awakening_level?: Number
|
||||
}
|
||||
}
|
||||
|
|
@ -70,7 +71,7 @@ const WeaponModal = ({
|
|||
|
||||
const [element, setElement] = useState(-1)
|
||||
|
||||
const [awakeningType, setAwakeningType] = useState(0)
|
||||
const [awakening, setAwakening] = useState<Awakening>()
|
||||
const [awakeningLevel, setAwakeningLevel] = useState(1)
|
||||
|
||||
const [primaryAxModifier, setPrimaryAxModifier] = useState(-1)
|
||||
|
|
@ -136,9 +137,10 @@ const WeaponModal = ({
|
|||
setFormValid(isValid)
|
||||
}
|
||||
|
||||
function receiveAwakeningValues(type: number, level: number) {
|
||||
setAwakeningType(type)
|
||||
function receiveAwakeningValues(id: string, level: number) {
|
||||
setAwakening(gridWeapon.object.awakenings.find((a) => a.id === id))
|
||||
setAwakeningLevel(level)
|
||||
setFormValid(true)
|
||||
}
|
||||
|
||||
function receiveElementValue(element: string) {
|
||||
|
|
@ -167,8 +169,8 @@ const WeaponModal = ({
|
|||
object.weapon.ax_strength2 = secondaryAxValue
|
||||
}
|
||||
|
||||
if (gridWeapon.object.awakening) {
|
||||
object.weapon.awakening_type = awakeningType
|
||||
if (gridWeapon.object.awakenings) {
|
||||
object.weapon.awakening_id = awakening?.id
|
||||
object.weapon.awakening_level = awakeningLevel
|
||||
}
|
||||
|
||||
|
|
@ -313,10 +315,12 @@ const WeaponModal = ({
|
|||
return (
|
||||
<section>
|
||||
<h3>{t('modals.weapon.subtitles.awakening')}</h3>
|
||||
<AwakeningSelect
|
||||
object="weapon"
|
||||
type={gridWeapon.awakening?.type}
|
||||
<AwakeningSelectWithInput
|
||||
dataSet={gridWeapon.object.awakenings}
|
||||
awakening={gridWeapon.awakening?.type}
|
||||
level={gridWeapon.awakening?.level}
|
||||
defaultAwakening={NO_AWAKENING}
|
||||
maxLevel={gridWeapon.object.max_awakening_level}
|
||||
onOpenChange={receiveAwakeningOpen}
|
||||
sendValidity={receiveValidity}
|
||||
sendValues={receiveAwakeningValues}
|
||||
|
|
@ -326,7 +330,7 @@ const WeaponModal = ({
|
|||
}
|
||||
|
||||
function handleOpenChange(open: boolean) {
|
||||
if (gridWeapon.object.ax || gridWeapon.object.awakening) {
|
||||
if (gridWeapon.object.ax || gridWeapon.object.awakenings) {
|
||||
setFormValid(false)
|
||||
} else {
|
||||
setFormValid(true)
|
||||
|
|
@ -387,7 +391,7 @@ const WeaponModal = ({
|
|||
{gridWeapon.object.element == 0 ? elementSelect() : ''}
|
||||
{[2, 3, 17, 24].includes(gridWeapon.object.series) ? keySelect() : ''}
|
||||
{gridWeapon.object.ax ? axSelect() : ''}
|
||||
{gridWeapon.awakening ? awakeningSelect() : ''}
|
||||
{gridWeapon.object.awakenings ? awakeningSelect() : ''}
|
||||
</div>
|
||||
<div className="DialogFooter" ref={footerRef}>
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ import Button from '~components/common/Button'
|
|||
import type { SearchableObject } from '~types'
|
||||
|
||||
import ax from '~data/ax'
|
||||
import { weaponAwakening } from '~data/awakening'
|
||||
|
||||
import PlusIcon from '~public/icons/Add.svg'
|
||||
import SettingsIcon from '~public/icons/Settings.svg'
|
||||
|
|
@ -88,7 +87,7 @@ const WeaponUnit = ({
|
|||
|
||||
return (
|
||||
weapon.ax ||
|
||||
weapon.awakening ||
|
||||
weapon.awakenings ||
|
||||
(weapon.series && [2, 3, 17, 22, 24].includes(weapon.series))
|
||||
)
|
||||
}
|
||||
|
|
@ -190,21 +189,16 @@ const WeaponUnit = ({
|
|||
function awakeningImage() {
|
||||
if (
|
||||
gridWeapon &&
|
||||
gridWeapon.object.awakening &&
|
||||
gridWeapon.object.awakenings &&
|
||||
gridWeapon.awakening &&
|
||||
gridWeapon.awakening.type > 0 &&
|
||||
gridWeapon.awakening.type != null
|
||||
gridWeapon.awakening.type
|
||||
) {
|
||||
const awakening = weaponAwakening.find(
|
||||
(awakening) => awakening.id === gridWeapon?.awakening?.type
|
||||
)
|
||||
const name = awakening?.name[locale]
|
||||
|
||||
const awakening = gridWeapon.awakening
|
||||
return (
|
||||
<img
|
||||
alt={`${name} Lv${gridWeapon.awakening.level}`}
|
||||
alt={`${awakening.type.name[locale]} Lv${gridWeapon.awakening.level}`}
|
||||
className="Awakening"
|
||||
src={`${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/awakening/weapon_${gridWeapon.awakening.type}.png`}
|
||||
src={`${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/awakening/${gridWeapon.awakening.type.slug}.png`}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,97 +1,10 @@
|
|||
export type Awakening = {
|
||||
id: number
|
||||
name: {
|
||||
[key: string]: string
|
||||
en: string
|
||||
ja: string
|
||||
}
|
||||
}
|
||||
export const characterAwakening: ItemSkill[] = [
|
||||
{
|
||||
id: 1,
|
||||
granblue_id: '',
|
||||
name: {
|
||||
en: 'Balanced',
|
||||
ja: 'バランス',
|
||||
},
|
||||
slug: 'balanced',
|
||||
minValue: 1,
|
||||
maxValue: 9,
|
||||
fractional: false,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
granblue_id: '',
|
||||
name: {
|
||||
en: 'Attack',
|
||||
ja: '攻撃',
|
||||
},
|
||||
slug: 'attack',
|
||||
minValue: 1,
|
||||
maxValue: 9,
|
||||
fractional: false,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
granblue_id: '',
|
||||
name: {
|
||||
en: 'Defense',
|
||||
ja: '防御',
|
||||
},
|
||||
slug: 'defense',
|
||||
minValue: 1,
|
||||
maxValue: 9,
|
||||
fractional: false,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
granblue_id: '',
|
||||
name: {
|
||||
en: 'Multiattack',
|
||||
ja: '連続攻撃',
|
||||
},
|
||||
slug: 'multiattack',
|
||||
minValue: 1,
|
||||
maxValue: 9,
|
||||
fractional: false,
|
||||
},
|
||||
]
|
||||
export const MAX_CHARACTER_AWAKENING_LEVEL = 9
|
||||
|
||||
export const weaponAwakening: ItemSkill[] = [
|
||||
{
|
||||
id: 1,
|
||||
granblue_id: '',
|
||||
name: {
|
||||
en: 'Attack',
|
||||
ja: '攻撃',
|
||||
},
|
||||
slug: 'attack',
|
||||
minValue: 1,
|
||||
maxValue: 15,
|
||||
fractional: false,
|
||||
export const NO_AWAKENING: Awakening = {
|
||||
id: '0',
|
||||
name: {
|
||||
en: 'No awakening',
|
||||
jp: '覚醒なし',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
granblue_id: '',
|
||||
name: {
|
||||
en: 'Defense',
|
||||
ja: '防御',
|
||||
},
|
||||
slug: 'defense',
|
||||
minValue: 1,
|
||||
maxValue: 15,
|
||||
fractional: false,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
granblue_id: '',
|
||||
name: {
|
||||
en: 'Special',
|
||||
ja: '特殊',
|
||||
},
|
||||
slug: 'special',
|
||||
minValue: 1,
|
||||
maxValue: 15,
|
||||
fractional: false,
|
||||
},
|
||||
]
|
||||
slug: 'no-awakening',
|
||||
}
|
||||
|
|
|
|||
11
types/Awakening.d.ts
vendored
Normal file
11
types/Awakening.d.ts
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
interface Awakening {
|
||||
id: string
|
||||
name: {
|
||||
[key: string]: string
|
||||
en: string
|
||||
jp: string
|
||||
}
|
||||
slug: string
|
||||
object_type?: string
|
||||
order: number
|
||||
}
|
||||
1
types/Character.d.ts
vendored
1
types/Character.d.ts
vendored
|
|
@ -35,6 +35,7 @@ interface Character {
|
|||
proficiency1: number
|
||||
proficiency2: number
|
||||
}
|
||||
awakenings: Awakening[]
|
||||
position?: number
|
||||
special: boolean
|
||||
}
|
||||
|
|
|
|||
2
types/GridCharacter.d.ts
vendored
2
types/GridCharacter.d.ts
vendored
|
|
@ -7,7 +7,7 @@ interface GridCharacter {
|
|||
over_mastery: CharacterOverMastery
|
||||
aetherial_mastery: ExtendedMastery
|
||||
awakening: {
|
||||
type: number
|
||||
type: Awakening
|
||||
level: number
|
||||
}
|
||||
perpetuity: boolean
|
||||
|
|
|
|||
2
types/GridWeapon.d.ts
vendored
2
types/GridWeapon.d.ts
vendored
|
|
@ -8,7 +8,7 @@ interface GridWeapon {
|
|||
weapon_keys?: Array<WeaponKey>
|
||||
ax?: Array<SimpleAxSkill>
|
||||
awakening?: {
|
||||
type: number
|
||||
type: Awakening
|
||||
level: number
|
||||
}
|
||||
}
|
||||
|
|
|
|||
3
types/Weapon.d.ts
vendored
3
types/Weapon.d.ts
vendored
|
|
@ -7,10 +7,11 @@ interface Weapon {
|
|||
proficiency: number
|
||||
max_level: number
|
||||
max_skill_level: number
|
||||
max_awakening_level: number
|
||||
series: number
|
||||
ax: boolean
|
||||
ax_type: number
|
||||
awakening: boolean
|
||||
awakenings: Awakening[]
|
||||
name: {
|
||||
[key: string]: string
|
||||
en: string
|
||||
|
|
|
|||
6
types/index.d.ts
vendored
6
types/index.d.ts
vendored
|
|
@ -63,10 +63,8 @@ interface GridCharacterObject {
|
|||
ring3: ExtendedMastery
|
||||
ring4: ExtendedMastery
|
||||
earring: ExtendedMastery
|
||||
awakening: {
|
||||
type?: number
|
||||
level?: number
|
||||
}
|
||||
awakening_id?: string
|
||||
awakening_level?: number
|
||||
transcendence_step: number
|
||||
perpetuity: boolean
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue