From bad870a546dce7415d22b654f0e16ee6737692fa Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Sun, 16 Apr 2023 03:48:47 -0700 Subject: [PATCH] Add reps for grid objects These reps act like the existing PartyRep except for Characters and Summons, as well as a new component just for Weapons. They only render the grid of objects and nothing else. Eventually PartyRep will use WeaponRep --- components/reps/CharacterRep/index.scss | 75 +++++++++++ components/reps/CharacterRep/index.tsx | 132 ++++++++++++++++++ components/reps/SummonRep/index.scss | 45 +++++++ components/reps/SummonRep/index.tsx | 172 ++++++++++++++++++++++++ components/reps/WeaponRep/index.scss | 46 +++++++ components/reps/WeaponRep/index.tsx | 106 +++++++++++++++ 6 files changed, 576 insertions(+) create mode 100644 components/reps/CharacterRep/index.scss create mode 100644 components/reps/CharacterRep/index.tsx create mode 100644 components/reps/SummonRep/index.scss create mode 100644 components/reps/SummonRep/index.tsx create mode 100644 components/reps/WeaponRep/index.scss create mode 100644 components/reps/WeaponRep/index.tsx diff --git a/components/reps/CharacterRep/index.scss b/components/reps/CharacterRep/index.scss new file mode 100644 index 00000000..acaf25ab --- /dev/null +++ b/components/reps/CharacterRep/index.scss @@ -0,0 +1,75 @@ +.CharacterRep { + aspect-ratio: 2/0.99; + border-radius: $card-corner; + grid-gap: $unit-half; /* add a gap of 8px between grid items */ + height: $rep-height; + + .Character { + background: var(--card-bg); + border-radius: 4px; + } + + .GridCharacters { + display: grid; /* make the right-images container a grid */ + grid-template-columns: repeat( + 4, + 1fr + ); /* create 3 columns, each taking up 1 fraction */ + gap: $unit-half; + } + + .Grid.Character { + aspect-ratio: 16 / 33; + box-sizing: border-box; + display: grid; + overflow: hidden; + + &.MC { + border-color: transparent; + border-width: 1px; + border-style: solid; + aspect-ratio: 32 / 66; + + img { + position: relative; + width: 100%; + height: 100%; + } + + &.fire { + background: var(--fire-hover-bg); + border-color: var(--fire-bg); + } + + &.water { + background: var(--water-hover-bg); + border-color: var(--water-bg); + } + + &.wind { + background: var(--wind-hover-bg); + border-color: var(--wind-bg); + } + + &.earth { + background: var(--earth-hover-bg); + border-color: var(--earth-bg); + } + + &.light { + background: var(--light-hover-bg); + border-color: var(--light-bg); + } + + &.dark { + background: var(--dark-hover-bg); + border-color: var(--dark-bg); + } + } + } + + .Grid.Character img[src*='jpg'] { + border-radius: 4px; + width: 100%; + } +} diff --git a/components/reps/CharacterRep/index.tsx b/components/reps/CharacterRep/index.tsx new file mode 100644 index 00000000..8d53442e --- /dev/null +++ b/components/reps/CharacterRep/index.tsx @@ -0,0 +1,132 @@ +import React, { useEffect, useState } from 'react' +import { useRouter } from 'next/router' +import { useTranslation } from 'next-i18next' +import 'fix-date' + +import './index.scss' + +interface Props { + job?: Job + gender?: number + element?: number + grid: GridArray +} + +const CHARACTERS_COUNT = 3 + +const CharacterRep = (props: Props) => { + // Localization for alt tags + const router = useRouter() + const { t } = useTranslation('common') + const locale = + router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en' + + // Component state + const [characters, setCharacters] = useState>({}) + const [grid, setGrid] = useState>({}) + + // On grid update + useEffect(() => { + const newCharacters = Array(CHARACTERS_COUNT) + const gridCharacters = Array(CHARACTERS_COUNT) + + if (props.grid) { + for (const [key, value] of Object.entries(props.grid)) { + if (value) { + newCharacters[value.position] = value.object + gridCharacters[value.position] = value + } + } + } + + setCharacters(newCharacters) + setGrid(gridCharacters) + }, [props.grid]) + + // Convert element to string + function numberToElement() { + switch (props.element) { + case 1: + return 'wind' + case 2: + return 'fire' + case 3: + return 'water' + case 4: + return 'earth' + case 5: + return 'dark' + case 6: + return 'light' + default: + return '' + } + } + + // Methods: Image generation + function generateMCImage() { + let source = '' + + if (props.job) { + const slug = props.job.name.en.replaceAll(' ', '-').toLowerCase() + const gender = props.gender == 1 ? 'b' : 'a' + source = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/job-portraits/${slug}_${gender}.png` + } + + return props.job && props.job.id !== '-1' ? ( + {props.job + ) : ( + '' + ) + } + + function generateGridImage(position: number) { + let url = '' + + const character = characters[position] + const gridCharacter = grid[position] + + if (character && gridCharacter) { + // Change the image based on the uncap level + let suffix = '01' + if (gridCharacter.transcendence_step > 0) suffix = '04' + else if (gridCharacter.uncap_level >= 5) suffix = '03' + else if (gridCharacter.uncap_level > 2) suffix = '02' + + if (character.element == 0) { + url = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/chara-main/${character.granblue_id}_${props.element}.jpg` + } else { + url = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/chara-main/${character.granblue_id}_${suffix}.jpg` + } + } + + return characters[position] ? ( + {characters[position]?.name[locale]} + ) : ( + '' + ) + } + + // Render + return ( +
+
    +
  • + {generateMCImage()} +
  • + {Array.from(Array(CHARACTERS_COUNT)).map((x, i) => { + return ( +
  • + {generateGridImage(i)} +
  • + ) + })} +
+
+ ) +} + +export default CharacterRep diff --git a/components/reps/SummonRep/index.scss b/components/reps/SummonRep/index.scss new file mode 100644 index 00000000..58e4a4cf --- /dev/null +++ b/components/reps/SummonRep/index.scss @@ -0,0 +1,45 @@ +.SummonRep { + aspect-ratio: 2/1.045; + border-radius: $card-corner; + display: grid; + grid-template-columns: 1fr 2.25fr; /* left column takes up 1 fraction, right column takes up 3 fractions */ + grid-gap: $unit-half; /* add a gap of 8px between grid items */ + height: $rep-height; + + .Summon { + background: var(--card-bg); + border-radius: 4px; + } + + .Main.Summon { + aspect-ratio: 56/97; + display: grid; + grid-column: 1 / 2; /* spans one column */ + } + + .GridSummons { + display: grid; /* make the right-images container a grid */ + grid-template-columns: repeat( + 2, + 1fr + ); /* create 3 columns, each taking up 1 fraction */ + grid-template-rows: repeat( + 2, + 1fr + ); /* create 3 rows, each taking up 1 fraction */ + gap: $unit-half; + // column-gap: $unit; + // row-gap: $unit-2x; + } + + .Grid.Summon { + aspect-ratio: 184 / 138; + display: grid; + } + + .Main.Summon img[src*='jpg'], + .Grid.Summon img[src*='jpg'] { + border-radius: 4px; + width: 100%; + } +} diff --git a/components/reps/SummonRep/index.tsx b/components/reps/SummonRep/index.tsx new file mode 100644 index 00000000..a1a377d5 --- /dev/null +++ b/components/reps/SummonRep/index.tsx @@ -0,0 +1,172 @@ +import React, { useEffect, useState } from 'react' +import { useRouter } from 'next/router' +import { useTranslation } from 'next-i18next' +import 'fix-date' + +import './index.scss' + +interface Props { + grid: { + mainSummon: GridSummon | undefined + friendSummon: GridSummon | undefined + allSummons: GridArray + } +} + +const SUMMONS_COUNT = 4 + +const SummonRep = (props: Props) => { + // Localization for alt tags + const router = useRouter() + const { t } = useTranslation('common') + const locale = + router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en' + + // Component state + const [mainSummon, setMainSummon] = useState() + const [summons, setSummons] = useState>({}) + const [grid, setGrid] = useState>({}) + + // On grid update + useEffect(() => { + const newSummons = Array(SUMMONS_COUNT) + const gridSummons = Array(SUMMONS_COUNT) + + if (props.grid.mainSummon) { + setMainSummon(props.grid.mainSummon) + } + + if (props.grid.allSummons) { + for (const [key, value] of Object.entries(props.grid.allSummons)) { + if (value) { + newSummons[value.position] = value.object + gridSummons[value.position] = value + } + } + } + + setSummons(newSummons) + setGrid(gridSummons) + }, [props.grid]) + + // Methods: Image generation + function generateMainImage() { + let url = '' + + const upgradedSummons = [ + '2040094000', + '2040100000', + '2040080000', + '2040098000', + '2040090000', + '2040084000', + '2040003000', + '2040056000', + '2040020000', + '2040034000', + '2040028000', + '2040027000', + '2040046000', + '2040047000', + ] + + if (mainSummon) { + // Change the image based on the uncap level + let suffix = '' + if (mainSummon.object.uncap.xlb && mainSummon.uncap_level == 6) { + if ( + mainSummon.transcendence_step >= 1 && + mainSummon.transcendence_step < 5 + ) { + suffix = '_03' + } else if (mainSummon.transcendence_step === 5) { + suffix = '_04' + } + } else if ( + upgradedSummons.indexOf(mainSummon.object.granblue_id.toString()) != + -1 && + mainSummon.uncap_level == 5 + ) { + suffix = '_02' + } + + url = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/summon-main/${mainSummon.object.granblue_id}${suffix}.jpg` + } + + return mainSummon ? ( + {mainSummon.object.name[locale]} + ) : ( + '' + ) + } + + function generateGridImage(position: number) { + let url = '' + + const summon = summons[position] + const gridSummon = grid[position] + + const upgradedSummons = [ + '2040094000', + '2040100000', + '2040080000', + '2040098000', + '2040090000', + '2040084000', + '2040003000', + '2040056000', + '2040020000', + '2040034000', + '2040028000', + '2040027000', + '2040046000', + '2040047000', + ] + + if (summon && gridSummon) { + // Change the image based on the uncap level + let suffix = '' + if (gridSummon.object.uncap.xlb && gridSummon.uncap_level == 6) { + if ( + gridSummon.transcendence_step >= 1 && + gridSummon.transcendence_step < 5 + ) { + suffix = '_03' + } else if (gridSummon.transcendence_step === 5) { + suffix = '_04' + } + } else if ( + upgradedSummons.indexOf(summon.granblue_id.toString()) != -1 && + gridSummon.uncap_level == 5 + ) { + suffix = '_02' + } + + url = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/summon-grid/${summon.granblue_id}${suffix}.jpg` + } + + return summons[position] ? ( + {summons[position]?.name[locale]} + ) : ( + '' + ) + } + + // Render + return ( +
+
{generateMainImage()}
+
    + {Array.from(Array(SUMMONS_COUNT)).map((x, i) => { + return ( +
  • + {generateGridImage(i + 1)} +
  • + ) + })} +
+
+ ) +} + +export default SummonRep diff --git a/components/reps/WeaponRep/index.scss b/components/reps/WeaponRep/index.scss new file mode 100644 index 00000000..40eee89f --- /dev/null +++ b/components/reps/WeaponRep/index.scss @@ -0,0 +1,46 @@ +.WeaponRep { + aspect-ratio: 2/0.95; + border-radius: $card-corner; + display: grid; + grid-template-columns: 1fr 3.39fr; /* left column takes up 1 fraction, right column takes up 3 fractions */ + grid-gap: $unit-half; /* add a gap of 8px between grid items */ + height: $rep-height; + + .Weapon { + background: var(--card-bg); + border-radius: 4px; + } + + .Mainhand.Weapon { + aspect-ratio: 73/153; + display: grid; + grid-column: 1 / 2; /* spans one column */ + max-height: 149px; + } + + .GridWeapons { + display: grid; /* make the right-images container a grid */ + grid-template-columns: repeat( + 3, + 1fr + ); /* create 3 columns, each taking up 1 fraction */ + grid-template-rows: repeat( + 3, + 1fr + ); /* create 3 rows, each taking up 1 fraction */ + gap: $unit-half; + // column-gap: $unit; + // row-gap: $unit-2x; + } + + .Grid.Weapon { + aspect-ratio: 280 / 160; + display: grid; + } + + .Mainhand.Weapon img[src*='jpg'], + .Grid.Weapon img[src*='jpg'] { + border-radius: 4px; + width: 100%; + } +} diff --git a/components/reps/WeaponRep/index.tsx b/components/reps/WeaponRep/index.tsx new file mode 100644 index 00000000..4cecdd29 --- /dev/null +++ b/components/reps/WeaponRep/index.tsx @@ -0,0 +1,106 @@ +import React, { useEffect, useState } from 'react' +import { useRouter } from 'next/router' +import { useTranslation } from 'next-i18next' +import 'fix-date' + +import './index.scss' + +interface Props { + grid: { + mainWeapon: GridWeapon | undefined + allWeapons: GridArray + } +} + +const WEAPONS_COUNT = 9 + +const WeaponRep = (props: Props) => { + // Localization for alt tags + const router = useRouter() + const { t } = useTranslation('common') + const locale = + router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en' + + // Component state + const [mainhand, setMainhand] = useState() + const [weapons, setWeapons] = useState>({}) + const [grid, setGrid] = useState>({}) + + // On grid update + useEffect(() => { + const newWeapons = Array(WEAPONS_COUNT) + const gridWeapons = Array(WEAPONS_COUNT) + + if (props.grid.mainWeapon) { + setMainhand(props.grid.mainWeapon) + } else { + setMainhand(undefined) + } + + if (props.grid.allWeapons) { + for (const [key, value] of Object.entries(props.grid.allWeapons)) { + if (value) { + newWeapons[value.position] = value.object + gridWeapons[value.position] = value + } + } + } + + setWeapons(newWeapons) + setGrid(gridWeapons) + }, [props.grid]) + + // Methods: Image generation + function generateMainhandImage() { + let url = '' + + if (mainhand && mainhand.object) { + if (mainhand.object.element == 0 && mainhand.element) { + url = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-main/${mainhand.object.granblue_id}_${mainhand.element}.jpg` + } else { + url = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-main/${mainhand.object.granblue_id}.jpg` + } + } + + return mainhand ? {mainhand.object.name[locale]} : '' + } + + function generateGridImage(position: number) { + let url = '' + + const weapon = weapons[position] + const gridWeapon = grid[position] + + if (weapon && gridWeapon) { + if (weapon.element == 0 && gridWeapon.element) { + url = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-grid/${weapon.granblue_id}_${gridWeapon.element}.jpg` + } else { + url = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-grid/${weapon.granblue_id}.jpg` + } + } + + return weapons[position] ? ( + {weapons[position]?.name[locale]} + ) : ( + '' + ) + } + + // Render + return ( +
+
{generateMainhandImage()}
+
    + {Array.from(Array(WEAPONS_COUNT)).map((x, i) => { + return ( +
  • + {generateGridImage(i)} +
  • + ) + })} +
+
+ ) +} + +export default WeaponRep