feat: added ability to handle sp variant
This commit is contained in:
@@ -77,6 +77,7 @@ export default function Operator() {
|
||||
const [voiceSrc, setVoiceSrc] = useState(null)
|
||||
const [isVoicePlaying, _setIsVoicePlaying] = useState(false)
|
||||
const isVoicePlayingRef = useRef(isVoicePlaying)
|
||||
const [spineAnimationList, setSpineAnimationList] = useState([])
|
||||
|
||||
const setVoiceLang = (value) => {
|
||||
voiceLangRef.current = value
|
||||
@@ -154,7 +155,7 @@ export default function Operator() {
|
||||
|
||||
useEffect(() => {
|
||||
if (config) {
|
||||
setTitle(getPartialName('name', config.codename[language]))
|
||||
setTitle(config.codename[language])
|
||||
}
|
||||
}, [config, language, key, setTitle])
|
||||
|
||||
@@ -180,6 +181,9 @@ export default function Operator() {
|
||||
fps: 60,
|
||||
defaultMix: 0.3,
|
||||
success: (player) => {
|
||||
setSpineAnimationList(
|
||||
player.skeleton.data.animations.map((e) => e.name)
|
||||
)
|
||||
if (
|
||||
player.skeleton.data.animations
|
||||
.map((e) => e.name)
|
||||
@@ -283,9 +287,9 @@ export default function Operator() {
|
||||
(animation) => {
|
||||
if (voiceLangRef.current) {
|
||||
let id = null
|
||||
if (animation === 'Idle') id = 'CN_011'
|
||||
if (animation === 'Interact') id = 'CN_034'
|
||||
if (animation === 'Special') id = 'CN_042'
|
||||
if (animation.startsWith('Idle')) id = 'CN_011'
|
||||
if (animation.startsWith('Interact')) id = 'CN_034'
|
||||
if (animation.startsWith('Special')) id = 'CN_042'
|
||||
if (id) {
|
||||
setCurrentVoiceId(id)
|
||||
setVoiceSrc(
|
||||
@@ -320,29 +324,15 @@ export default function Operator() {
|
||||
const spineSettings = [
|
||||
{
|
||||
name: 'animation',
|
||||
options: [
|
||||
{
|
||||
name: 'idle',
|
||||
onClick: () => setSpineAnimation('Idle'),
|
||||
options: spineAnimationList.map((name) => {
|
||||
return {
|
||||
name,
|
||||
onClick: () => setSpineAnimation(name),
|
||||
activeRule: () => {
|
||||
return spineAnimationName === 'Idle'
|
||||
return spineAnimationName === name
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'interact',
|
||||
onClick: () => setSpineAnimation('Interact'),
|
||||
activeRule: () => {
|
||||
return spineAnimationName === 'Interact'
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'special',
|
||||
onClick: () => setSpineAnimation('Special'),
|
||||
activeRule: () => {
|
||||
return spineAnimationName === 'Special'
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'voice',
|
||||
|
||||
@@ -16,6 +16,7 @@ export default class Player {
|
||||
#resetTime = window.performance.now()
|
||||
#isPlayingInteract = false
|
||||
#spine
|
||||
#animationList = []
|
||||
#default = {
|
||||
fps: 60,
|
||||
padding: {
|
||||
@@ -62,6 +63,9 @@ export default class Player {
|
||||
fps: 60,
|
||||
defaultMix: 0,
|
||||
success: function (widget) {
|
||||
_this.#animationList = widget.skeleton.data.animations.map(
|
||||
(e) => e.name
|
||||
)
|
||||
if (
|
||||
widget.skeleton.data.animations
|
||||
.map((e) => e.name)
|
||||
@@ -70,10 +74,15 @@ export default class Player {
|
||||
) {
|
||||
widget.animationState.setAnimation(0, 'Start', false)
|
||||
}
|
||||
widget.animationState.addAnimation(0, 'Idle', true, 0)
|
||||
widget.animationState.addAnimation(
|
||||
0,
|
||||
_this.#selectRandomAnimation('Idle'),
|
||||
true,
|
||||
0
|
||||
)
|
||||
widget.animationState.addListener({
|
||||
end: (e) => {
|
||||
if (e.animation.name == 'Interact') {
|
||||
if (e.animation.name.startsWith('Interact')) {
|
||||
_this.#isPlayingInteract = false
|
||||
}
|
||||
},
|
||||
@@ -85,13 +94,13 @@ export default class Player {
|
||||
_this.#resetTime = performance.now()
|
||||
let entry = widget.animationState.setAnimation(
|
||||
0,
|
||||
'Special',
|
||||
_this.#selectRandomAnimation('Special'),
|
||||
false
|
||||
)
|
||||
entry.mixDuration = 0.3
|
||||
widget.animationState.addAnimation(
|
||||
0,
|
||||
'Idle',
|
||||
_this.#selectRandomAnimation('Idle'),
|
||||
true,
|
||||
0
|
||||
)
|
||||
@@ -105,11 +114,16 @@ export default class Player {
|
||||
_this.#isPlayingInteract = true
|
||||
let entry = widget.animationState.setAnimation(
|
||||
0,
|
||||
'Interact',
|
||||
_this.#selectRandomAnimation('Interact'),
|
||||
false
|
||||
)
|
||||
entry.mixDuration = 0.3
|
||||
widget.animationState.addAnimation(0, 'Idle', true, 0)
|
||||
widget.animationState.addAnimation(
|
||||
0,
|
||||
_this.#selectRandomAnimation('Idle'),
|
||||
true,
|
||||
0
|
||||
)
|
||||
}
|
||||
document.dispatchEvent(Events.Ready.handler())
|
||||
},
|
||||
@@ -124,10 +138,7 @@ export default class Player {
|
||||
|
||||
success() {
|
||||
this.#loadViewport()
|
||||
updateHTMLOptions(
|
||||
this.#spine.skeleton.data.animations.map((e) => e.name),
|
||||
'animation-selection'
|
||||
)
|
||||
updateHTMLOptions(this.#animationList, 'animation-selection')
|
||||
}
|
||||
|
||||
resetPadding() {
|
||||
@@ -176,6 +187,13 @@ export default class Player {
|
||||
})
|
||||
}
|
||||
|
||||
#selectRandomAnimation(name) {
|
||||
const animationList = this.#animationList.filter((animation) =>
|
||||
animation.startsWith(name)
|
||||
)
|
||||
return animationList[Math.floor(Math.random() * animationList.length)]
|
||||
}
|
||||
|
||||
get usePadding() {
|
||||
return this.#config.usePadding
|
||||
}
|
||||
|
||||
@@ -39,6 +39,10 @@ module:
|
||||
title:
|
||||
zh-CN: '明日方舟:'
|
||||
en-US: 'Arknights: '
|
||||
sp_filename_prefix: sp_
|
||||
sp_title:
|
||||
zh-CN: '「SP」 '
|
||||
en-US: '[SP] '
|
||||
project_json:
|
||||
project_json: project.json
|
||||
preview_jpg: preview.jpg
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
export type TitleLanguages = {
|
||||
'zh-CN': string
|
||||
'en-US': string
|
||||
}
|
||||
|
||||
export type Config = {
|
||||
site_id: string
|
||||
total_size: number
|
||||
@@ -44,10 +49,9 @@ export type Config = {
|
||||
Texture2D: string
|
||||
character_table_json: string
|
||||
skin_table_json: string
|
||||
title: {
|
||||
'zh-CN': string
|
||||
'en-US': string
|
||||
}
|
||||
title: TitleLanguages
|
||||
sp_filename_prefix: string
|
||||
sp_title: TitleLanguages
|
||||
}
|
||||
project_json: {
|
||||
project_json: string
|
||||
|
||||
@@ -64,6 +64,7 @@ ines_melodic_flutter: !include config/ines_melodic_flutter.yaml
|
||||
wisadel_supernova: !include config/wisadel_supernova.yaml
|
||||
archetto_glory_of_the_devout: !include config/archetto_glory_of_the_devout.yaml
|
||||
kroos_moonlit_voyage: !include config/kroos_moonlit_voyage.yaml
|
||||
kroos_moonlit_voyage_sp: !include config/kroos_moonlit_voyage_sp.yaml
|
||||
exusiai_the_new_covenant: !include config/exusiai_the_new_covenant.yaml
|
||||
ascalon_phototaxis: !include config/ascalon_phototaxis.yaml
|
||||
lin_summer_flowers_fa137: !include config/lin_summer_flowers_fa137.yaml
|
||||
|
||||
6
packages/operator/config/kroos_moonlit_voyage_sp.yaml
Normal file
6
packages/operator/config/kroos_moonlit_voyage_sp.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
codename:
|
||||
zh-CN: 星月漂流记 · 克洛丝
|
||||
en-US: Moonlit Voyage / Kroos
|
||||
official_id: '202504992'
|
||||
isSP: true
|
||||
viewport_top: 2
|
||||
@@ -1,3 +1,4 @@
|
||||
import { TitleLanguages } from './../config/types'
|
||||
import path from 'node:path'
|
||||
import { yaml, file, alphaComposite } from '@aklive2d/libs'
|
||||
import config from '@aklive2d/config'
|
||||
@@ -53,8 +54,10 @@ export const generateAssetsJson = async (
|
||||
extractedDir: string,
|
||||
targetDir: string,
|
||||
_opts: {
|
||||
isSP?: boolean
|
||||
useSymLink?: boolean
|
||||
} = {
|
||||
isSP: false,
|
||||
useSymLink: true,
|
||||
}
|
||||
) => {
|
||||
@@ -64,7 +67,7 @@ export const generateAssetsJson = async (
|
||||
* Special Cases:
|
||||
* - ines_melodic_flutter
|
||||
*/
|
||||
filename = getActualFilename(filename, extractedDir)
|
||||
filename = getActualFilename(filename, extractedDir, _opts.isSP)
|
||||
|
||||
const skelFilename = findSkel(filename, extractedDir)
|
||||
const atlasFilename = `${filename}.atlas`
|
||||
@@ -160,8 +163,23 @@ const generateMapping = () => {
|
||||
type === 'skin'
|
||||
? skinEntry.skinId.replace(/@/, '_')
|
||||
: `${skinEntry.charId}_2`
|
||||
|
||||
const regions = Object.keys(
|
||||
operator.codename
|
||||
) as (keyof TitleLanguages)[]
|
||||
if (operator.isSP) {
|
||||
regions.forEach((region: keyof TitleLanguages) => {
|
||||
operator.codename[region] =
|
||||
`${config.module.operator.sp_title[region]}${operator.codename[region]}`
|
||||
})
|
||||
}
|
||||
// add title
|
||||
operator.title = `${config.module.operator.title['en-US']}${operator.codename['en-US']} - ${config.module.operator.title['zh-CN']}${operator.codename['zh-CN']}`
|
||||
operator.title = regions
|
||||
.map(
|
||||
(region: keyof TitleLanguages) =>
|
||||
`${config.module.operator.title[region]}${operator.codename[region]}`
|
||||
)
|
||||
.join(' - ')
|
||||
|
||||
// add type
|
||||
operator.type = operatorInfo.type
|
||||
|
||||
@@ -112,6 +112,9 @@ const generateAssets = async (name: string) => {
|
||||
await generateAssetsJson(
|
||||
operators[name].filename,
|
||||
extractedDir,
|
||||
getDistFolder(name)
|
||||
getDistFolder(name),
|
||||
{
|
||||
isSP: operators[name].isSP,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -168,7 +168,13 @@ export const findCodename = (
|
||||
return codename
|
||||
}
|
||||
|
||||
export const getActualFilename = (filename: string, dir: string) => {
|
||||
export const getActualFilename = (
|
||||
filename: string,
|
||||
dir: string,
|
||||
isSP: boolean = false
|
||||
) => {
|
||||
if (isSP)
|
||||
filename = `${config.module.operator.sp_filename_prefix}${filename}`
|
||||
const files = file.readdirSync(dir)
|
||||
const actualFilename = files.find((e) => {
|
||||
const name = path.parse(e).name
|
||||
|
||||
@@ -21,6 +21,7 @@ export interface OperatorConfig {
|
||||
date: string
|
||||
voice_id: string | null
|
||||
color: string
|
||||
isSP: boolean // kroos_moonlit_voyage_sp
|
||||
}
|
||||
|
||||
export type Config = {
|
||||
|
||||
@@ -21,6 +21,8 @@ interface DirectoryOperatorConfig extends OperatorConfig {
|
||||
workshopId: string | null
|
||||
}
|
||||
|
||||
const SPINE_FILENAME_PREFIX = 'dyn_illust_'
|
||||
|
||||
export const copyShowcaseData = (
|
||||
name: string,
|
||||
{
|
||||
@@ -37,7 +39,13 @@ export const copyShowcaseData = (
|
||||
)
|
||||
const spineFilenames = file
|
||||
.readdirSync(operatorAssetsDir)
|
||||
.filter((item) => item.startsWith('dyn_illust_'))
|
||||
.filter((item) =>
|
||||
item.startsWith(
|
||||
operators[name].isSP
|
||||
? `${config.module.operator.sp_filename_prefix}${SPINE_FILENAME_PREFIX}`
|
||||
: SPINE_FILENAME_PREFIX
|
||||
)
|
||||
)
|
||||
const q = [
|
||||
{
|
||||
fn: file.symlink,
|
||||
@@ -113,7 +121,8 @@ export const copyShowcaseData = (
|
||||
})
|
||||
const filename = getActualFilename(
|
||||
operators[name].filename,
|
||||
getExtractedFolder(name)
|
||||
getExtractedFolder(name),
|
||||
operators[name].isSP
|
||||
)
|
||||
const buildConfig = {
|
||||
insight_id: config.insight.id,
|
||||
@@ -201,7 +210,8 @@ export const copyDirectoryData = async ({
|
||||
const curD = cur as DirectoryOperatorConfig
|
||||
curD.filename = getActualFilename(
|
||||
operators[curD.link].filename,
|
||||
getExtractedFolder(curD.link)
|
||||
getExtractedFolder(curD.link),
|
||||
operators[curD.link].isSP
|
||||
)
|
||||
curD.use_json = findSkel(
|
||||
curD.filename,
|
||||
|
||||
Reference in New Issue
Block a user