Add weapon page
This commit is contained in:
@@ -34,7 +34,7 @@ function adjustColor(color: unknown): string | undefined {
|
||||
const normalizedColor = color.toUpperCase()
|
||||
|
||||
if (normalizedColor.startsWith('#FEDF4C')) {
|
||||
return '#e6c010'
|
||||
return '#d1af15'
|
||||
}
|
||||
|
||||
if (normalizedColor.startsWith('#FFC741')) {
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import { Box, Flex } from 'theme-ui'
|
||||
import { assetsBucketBaseUrl } from '../../lib/consts'
|
||||
import { repeat } from '../../lib/utils'
|
||||
|
||||
interface WeaponIconProps {
|
||||
icon: string
|
||||
rarity?: number
|
||||
}
|
||||
|
||||
const starSize = 16
|
||||
|
||||
const WeaponIcon = ({ icon: fileName, rarity }: WeaponIconProps) => {
|
||||
return (
|
||||
<Box sx={{ width: 112 }}>
|
||||
<Box
|
||||
sx={{
|
||||
borderRadius: 4,
|
||||
width: (128 / 8) * 7,
|
||||
height: (112 / 8) * 7,
|
||||
background: ` no-repeat 100% 100% /100% url(${assetsBucketBaseUrl}/raw/weaponicons/${fileName}.png), ${getBackgroundByRarity(
|
||||
rarity
|
||||
)}`
|
||||
}}
|
||||
/>
|
||||
{rarity != null && (
|
||||
<Flex sx={{ marginTop: -starSize / 2, justifyContent: 'center', width: '100%' }}>
|
||||
{repeat(rarity, index => (
|
||||
<Box
|
||||
key={index}
|
||||
sx={{
|
||||
width: starSize,
|
||||
height: starSize,
|
||||
background: `no-repeat 50% / 100% url(${assetsBucketBaseUrl}/raw-misc/StarBig.png)`
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Flex>
|
||||
)}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default WeaponIcon
|
||||
|
||||
function getBackgroundByRarity(rarity?: number) {
|
||||
switch (rarity) {
|
||||
case 6:
|
||||
return 'linear-gradient(180deg, #e18434, #fbd977)'
|
||||
case 5:
|
||||
return 'linear-gradient(transparent, #c86de0),#7630a3'
|
||||
case 4:
|
||||
case 3:
|
||||
return 'linear-gradient(transparent, #0ec1eb), #2368b1'
|
||||
case 2:
|
||||
return `linear-gradient(transparent, #52c389),#31756c`
|
||||
default:
|
||||
return 'transparent'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export function repeat<N>(count: number, rendererFn: (index: number) => N) {
|
||||
const result: Array<N> = []
|
||||
for (let i = 0; i < count; i++) {
|
||||
result.push(rendererFn(i))
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -146,39 +146,44 @@ export interface BattlesuitSubSkill {
|
||||
export interface WeaponCatalogItem {
|
||||
id: string
|
||||
name: string
|
||||
icon: string
|
||||
type: WeaponType
|
||||
maxRarity: 1 | 2 | 3 | 4 | 5
|
||||
maxRarity: 1 | 2 | 3 | 4 | 5 | 6
|
||||
}
|
||||
|
||||
export interface RootWeaponData {
|
||||
id: string
|
||||
weapons: WeaponData[]
|
||||
}
|
||||
|
||||
export interface WeaponData {
|
||||
id: string
|
||||
weapons: {
|
||||
rarity: 1 | 2 | 3 | 4 | 5 | 6
|
||||
maxLv: number
|
||||
type: WeaponType
|
||||
name: string
|
||||
description: string
|
||||
icon: string
|
||||
hpBase: number
|
||||
hpAdd: number
|
||||
spBase: number
|
||||
spAdd: number
|
||||
attackBase: number
|
||||
attackAdd: number
|
||||
defenceBase: number
|
||||
defenceAdd: number
|
||||
criticalBase: number
|
||||
criticalAdd: number
|
||||
resistanceBase: number
|
||||
resistanceAdd: number
|
||||
skills: WeaponSkill[]
|
||||
rankUpMaterials: {
|
||||
id: string
|
||||
rarity: number
|
||||
maxLv: number
|
||||
type: WeaponType
|
||||
title: string
|
||||
description: string
|
||||
icon: string
|
||||
hpBase: number
|
||||
hpAdd: number
|
||||
spBase: number
|
||||
spAdd: number
|
||||
attackBase: number
|
||||
attackAdd: number
|
||||
defenceBase: number
|
||||
defenceAdd: number
|
||||
criticalBase: number
|
||||
criticalAdd: number
|
||||
resistanceBase: number
|
||||
resistanceAdd: number
|
||||
skills: WeaponSkill[]
|
||||
rankUpMaterials: {
|
||||
id: string
|
||||
amount: number
|
||||
}[]
|
||||
}
|
||||
amount: number
|
||||
}[]
|
||||
powerType: number
|
||||
}
|
||||
|
||||
export interface WeaponSkill {
|
||||
id: string
|
||||
name: string
|
||||
@@ -191,4 +196,10 @@ export interface WeaponSkill {
|
||||
type: TagType
|
||||
comment: string
|
||||
}[]
|
||||
param1: number
|
||||
param1Add: number
|
||||
param2: number
|
||||
param2Add: number
|
||||
param3: number
|
||||
param3Add: number
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ describe('compileWeaponData', () => {
|
||||
])
|
||||
})
|
||||
|
||||
expect(targetItem!.weapons[3]).toMatchObject({
|
||||
expect(targetItem?.weapons[3]).toMatchObject({
|
||||
skills: [
|
||||
{
|
||||
id: '1221'
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { RootWeaponData, WeaponData } from '../../data/types'
|
||||
import { getRawEquipmentSkillDataMap, RawEquipmentSkillData } from '../raw/equipmentSkillData'
|
||||
import { getRawWeaponDataMap } from '../raw/weaponData'
|
||||
import { convertTagType, convertWeaponType, getText } from './utils'
|
||||
|
||||
export function compileWeaponData() {
|
||||
export function compileWeaponData(): RootWeaponData[] {
|
||||
const rawWeaponDataMap = getRawWeaponDataMap()
|
||||
const rawEquipmentSkillDataMap = getRawEquipmentSkillDataMap()
|
||||
|
||||
@@ -20,7 +21,13 @@ export function compileWeaponData() {
|
||||
const rawSkill = rawEquipmentSkillDataMap[skillId]
|
||||
skills.push({
|
||||
id: skillId,
|
||||
...convertWeaponSkill(rawSkill)
|
||||
...convertWeaponSkill(rawSkill),
|
||||
param1: rawData.Prop1Param1,
|
||||
param1Add: rawData.Prop1Param1Add,
|
||||
param2: rawData.Prop1Param2,
|
||||
param2Add: rawData.Prop1Param2Add,
|
||||
param3: rawData.Prop1Param3,
|
||||
param3Add: rawData.Prop1Param3Add
|
||||
})
|
||||
}
|
||||
if (rawData.Prop2ID !== 0) {
|
||||
@@ -28,7 +35,13 @@ export function compileWeaponData() {
|
||||
const rawSkill = rawEquipmentSkillDataMap[skillId]
|
||||
skills.push({
|
||||
id: skillId,
|
||||
...convertWeaponSkill(rawSkill)
|
||||
...convertWeaponSkill(rawSkill),
|
||||
param1: rawData.Prop2Param1,
|
||||
param1Add: rawData.Prop2Param1Add,
|
||||
param2: rawData.Prop2Param2,
|
||||
param2Add: rawData.Prop2Param2Add,
|
||||
param3: rawData.Prop2Param3,
|
||||
param3Add: rawData.Prop2Param3Add
|
||||
})
|
||||
}
|
||||
if (rawData.Prop3ID !== 0) {
|
||||
@@ -36,16 +49,22 @@ export function compileWeaponData() {
|
||||
const rawSkill = rawEquipmentSkillDataMap[skillId]
|
||||
skills.push({
|
||||
id: skillId,
|
||||
...convertWeaponSkill(rawSkill)
|
||||
...convertWeaponSkill(rawSkill),
|
||||
param1: rawData.Prop3Param1,
|
||||
param1Add: rawData.Prop3Param1Add,
|
||||
param2: rawData.Prop3Param2,
|
||||
param2Add: rawData.Prop3Param2Add,
|
||||
param3: rawData.Prop3Param3,
|
||||
param3Add: rawData.Prop3Param3Add
|
||||
})
|
||||
}
|
||||
const icon = rawData.IconPath.split('/').pop()
|
||||
const icon = rawData.IconPath.split('/').pop()!
|
||||
weaponList.push({
|
||||
id,
|
||||
rarity: rawData.Rarity,
|
||||
maxLv: rawData.MaxLv,
|
||||
type: convertWeaponType(rawData.BaseType),
|
||||
title: getText(rawData.DisplayTitle),
|
||||
name: getText(rawData.DisplayTitle),
|
||||
description: getText(rawData.DisplayDescription),
|
||||
icon,
|
||||
hpBase: rawData.HPBase,
|
||||
@@ -71,14 +90,14 @@ export function compileWeaponData() {
|
||||
powerType: rawData.PowerType
|
||||
})
|
||||
return map
|
||||
}, new Map())
|
||||
}, new Map<string, WeaponData[]>())
|
||||
|
||||
const weaponIdList = [...rawWeaponMainIdWeaponDataMap.keys()]
|
||||
|
||||
return weaponIdList.map(weaponId => {
|
||||
return {
|
||||
id: weaponId,
|
||||
weapons: rawWeaponMainIdWeaponDataMap.get(weaponId)
|
||||
weapons: rawWeaponMainIdWeaponDataMap.get(weaponId) || []
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ export type RawWeaponDataMap = {
|
||||
}
|
||||
|
||||
export interface RawWeaponData {
|
||||
Rarity: number
|
||||
Rarity: 1 | 2 | 3 | 4 | 5 | 6
|
||||
MaxRarity: number
|
||||
SubRarity: number
|
||||
SubMaxRarity: number
|
||||
|
||||
@@ -3,6 +3,7 @@ const HomePage = () => {
|
||||
<div>
|
||||
<h1>V2 Pre</h1>
|
||||
<a href="/v2-pre/battlesuits">Battlesuits</a>
|
||||
<a href="/v2-pre/weapons">Weapons</a>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
import { NextPageContext } from 'next'
|
||||
import { Box, Card, Flex, Heading } from 'theme-ui'
|
||||
import FormattedText from '../../../components/v2-pre/FormattedText'
|
||||
import { formatSubSkillInfo } from '../../../lib/v2-pre/data/formatText'
|
||||
import { loadWeaponCatalog, loadWeaponData } from '../../../lib/v2-pre/server/loadData'
|
||||
import { RootWeaponData, SkillTagItem } from '../../../lib/v2-pre/data/types'
|
||||
import { Fragment } from 'react'
|
||||
import TagIcon from '../../../components/v2-pre/TagIcon'
|
||||
import { getWeaponTypeLabel } from '../../../lib/v2-pre/data/text'
|
||||
import WeaponTypeIcon from '../../../components/v2-pre/WeaponTypeIcon'
|
||||
import WeaponIcon from '../../../components/v2-pre/WeaponIcon'
|
||||
|
||||
interface WeaponShowPageProps {
|
||||
rootWeapon: RootWeaponData
|
||||
}
|
||||
|
||||
const WeaponShowPage = ({ rootWeapon }: WeaponShowPageProps) => {
|
||||
const weapon = rootWeapon.weapons[rootWeapon.weapons.length - 1]
|
||||
return (
|
||||
<Box>
|
||||
<Heading as="h1">{weapon.name}</Heading>
|
||||
|
||||
<Box mb={3}>
|
||||
<WeaponIcon key={weapon.id} icon={weapon.icon} rarity={weapon.rarity} />
|
||||
</Box>
|
||||
|
||||
<Card mb={3}>
|
||||
<Box sx={{ p: 1, borderBottom: 'default' }}>{weapon.description}</Box>
|
||||
<Flex sx={{ alignItems: 'center', p: 1, borderBottom: 'default' }}>
|
||||
<WeaponTypeIcon type={weapon.type} />
|
||||
<Box ml={1}>{getWeaponTypeLabel(weapon.type)}</Box>
|
||||
</Flex>
|
||||
<Box sx={{ p: 1 }}>
|
||||
ATK : {Math.floor(weapon.attackBase + weapon.attackAdd * (weapon.maxLv - 1))} / CRT :{' '}
|
||||
{Math.floor(weapon.criticalBase + weapon.criticalAdd * (weapon.maxLv - 1))} (at Max Lv {weapon.maxLv})
|
||||
</Box>
|
||||
</Card>
|
||||
|
||||
<Heading as="h2">Skills</Heading>
|
||||
<Card>
|
||||
{weapon.skills.map(skill => {
|
||||
return (
|
||||
<Fragment key={skill.id}>
|
||||
<Box sx={{ borderBottom: 'default' }}>
|
||||
<Heading
|
||||
as="h3"
|
||||
sx={{
|
||||
p: 1,
|
||||
m: 1
|
||||
}}
|
||||
>
|
||||
{skill.name}
|
||||
</Heading>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
p: 1,
|
||||
borderBottom: 'default',
|
||||
'&:last-child': {
|
||||
borderBottom: 'none'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<FormattedText>
|
||||
{formatSubSkillInfo({
|
||||
info: skill.info,
|
||||
maxLv: weapon.maxLv,
|
||||
paramBase1: skill.param1,
|
||||
paramBase2: skill.param2,
|
||||
paramBase3: skill.param3,
|
||||
paramAdd1: skill.param1Add,
|
||||
paramAdd2: skill.param2Add,
|
||||
paramAdd3: skill.param3Add
|
||||
})}
|
||||
</FormattedText>
|
||||
</Box>
|
||||
</Fragment>
|
||||
)
|
||||
})}
|
||||
</Card>
|
||||
{/* <pre>{JSON.stringify(weapon, null, 2)}</pre> */}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default WeaponShowPage
|
||||
|
||||
export async function getStaticProps({ locale, params }: NextPageContext & { params: { weaponId: string } }) {
|
||||
const rootWeapon = loadWeaponData(params.weaponId)
|
||||
|
||||
return {
|
||||
props: { rootWeapon }
|
||||
}
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const weaponCatalog = loadWeaponCatalog()
|
||||
|
||||
return {
|
||||
paths: weaponCatalog.map(catalogItem => {
|
||||
return {
|
||||
params: { weaponId: catalogItem.id }
|
||||
}
|
||||
}),
|
||||
fallback: false
|
||||
}
|
||||
}
|
||||
|
||||
interface SkillTagBoxProps {
|
||||
tag: SkillTagItem
|
||||
}
|
||||
|
||||
const SkillTagItem = ({ tag }: SkillTagBoxProps) => {
|
||||
return <TagIcon type={tag.type} strength={tag.strength} comment={tag.comment} size="sm" />
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/** @jsxImportSource theme-ui */
|
||||
import { Box, Card } from '@theme-ui/components'
|
||||
import { NextPageContext } from 'next'
|
||||
import { Flex, Link } from 'theme-ui'
|
||||
import { loadWeaponCatalog } from '../../../lib/v2-pre/server/loadData'
|
||||
import { WeaponCatalogItem } from '../../../lib/v2-pre/data/types'
|
||||
import WeaponIcon from '../../../components/v2-pre/WeaponIcon'
|
||||
|
||||
interface BattlesuitListPageProps {
|
||||
weaponCatalog: WeaponCatalogItem[]
|
||||
}
|
||||
|
||||
const BattlesuitListPage = ({ weaponCatalog }: BattlesuitListPageProps) => {
|
||||
return (
|
||||
<Box>
|
||||
<h1>Weapons</h1>
|
||||
<Flex sx={{ flexWrap: 'wrap' }}>
|
||||
{weaponCatalog
|
||||
.filter(filterWeapons)
|
||||
.sort(sortWeapons)
|
||||
.map(weapon => {
|
||||
return (
|
||||
<Box key={weapon.id} m={2}>
|
||||
<Link href={`/v2-pre/weapons/${weapon.id}`}>
|
||||
<Card p={1}>
|
||||
<Flex sx={{ justifyContent: 'center' }}>
|
||||
<WeaponIcon icon={weapon.icon} rarity={weapon.maxRarity} />
|
||||
</Flex>
|
||||
<Box
|
||||
sx={{
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
px: 1,
|
||||
width: 112,
|
||||
textAlign: 'center'
|
||||
}}
|
||||
>
|
||||
{weapon.name}
|
||||
</Box>
|
||||
{/* {weapon.id} */}
|
||||
</Card>
|
||||
</Link>
|
||||
</Box>
|
||||
)
|
||||
})}
|
||||
</Flex>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default BattlesuitListPage
|
||||
|
||||
export async function getStaticProps({ locale }: NextPageContext) {
|
||||
const weaponCatalog = loadWeaponCatalog()
|
||||
|
||||
return {
|
||||
props: { weaponCatalog }
|
||||
}
|
||||
}
|
||||
|
||||
function filterWeapons(weapon: WeaponCatalogItem) {
|
||||
// Valid weapons
|
||||
// 24XXX EX Weapons
|
||||
// 21XXX Sprit Weapon
|
||||
// 19XXX Alloy Weapons
|
||||
// 10XXX Regular Weapons
|
||||
|
||||
// Event Weapon?
|
||||
if (weapon.id.startsWith('18') && weapon.id.length === 5) {
|
||||
return false
|
||||
}
|
||||
// Test Data?
|
||||
if (weapon.id.startsWith('1023') && weapon.id.length === 6) {
|
||||
return false
|
||||
}
|
||||
// Unreleased(Number looks good but lacking info)
|
||||
switch (weapon.id) {
|
||||
case '24000': // 2nd relic alpha
|
||||
case '24040': // Removed 7 Thunders
|
||||
case '24034': // PE Temp Lance
|
||||
case '24024': // keqing ex
|
||||
case '20765': // ?
|
||||
case '10243': // No Name
|
||||
case '10219': // ?
|
||||
case '10179': // ?
|
||||
return false
|
||||
}
|
||||
|
||||
// Divine Keys
|
||||
switch (weapon.id) {
|
||||
case '20000': // Taixuan Sword
|
||||
case '10248': // Abyss Flower
|
||||
case '10267': // Eden's star anti entropy
|
||||
case '10263': // 7 Thunders
|
||||
case '10247': // Abyss
|
||||
case '10235': // Fenghuang pistols
|
||||
case '10234': // Fenghuang fists
|
||||
case '10223': // Eden twin stars
|
||||
case '10212': // Pledge of Sakura
|
||||
case '10211': // Pledge of Judah
|
||||
case '10209': // Might of an utu
|
||||
case '10199': // Cleaver of Shamash
|
||||
case '10163': // Shamash pistols
|
||||
// The below don't have duplicated data
|
||||
// case '10180': // Eden's star
|
||||
// case '10169': // Taxiuan fists
|
||||
// case '10158': // Jizo Mitama
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
function sortWeapons(a: WeaponCatalogItem, b: WeaponCatalogItem) {
|
||||
let aId = parseInt(a.id)
|
||||
|
||||
const aTypeOrder = aId > 21000 && aId < 22000 ? 2 : aId > 19000 && aId < 20000 ? 1 : 0
|
||||
|
||||
let bId = parseInt(b.id)
|
||||
const bTypeOrder = bId > 21000 && bId < 22000 ? 2 : bId > 19000 && bId < 20000 ? 1 : 0
|
||||
if (aTypeOrder !== bTypeOrder) {
|
||||
return aTypeOrder - bTypeOrder
|
||||
}
|
||||
|
||||
return bId - aId
|
||||
}
|
||||
@@ -52,6 +52,7 @@ function writeWeaponData() {
|
||||
return {
|
||||
id: weapon.id,
|
||||
name: maxedWeapon.name,
|
||||
icon: maxedWeapon.icon,
|
||||
type: maxedWeapon.type,
|
||||
maxRarity: maxedWeapon.rarity
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user