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