import fs from 'fs'
import path from 'path'
let filePath = "D:\\新建文件夹\\chromeDnowload\\UnityCNLive2DExtractor\\新建文件夹\\assets\\res\\character"
let outPath = "D:\\新建文件夹\\chromeDnowload\\UnityCNLive2DExtractor\\新建文件夹\\assets\\res\\新建文件夹"
let wavPath = "D:\\新建文件夹\\qq\\MobileFile\\Voice\\Voice\\新建文件夹"
const characterNameRexg = /\\character\\([-\w]+)\\l2d/
const loginAndMouse = [
{
VarFloats: [
{
"Name": "login",
"Type": 1,
"Code": "lower 1"
},
{
"Name": "mouse",
"Type": 1,
"Code": "lower 1"
}
],
Command: "mouse_tracking enable"
},
{
VarFloats: [
{
"Name": "login",
"Type": 1,
"Code": "lower 1"
},
{
"Name": "mouse",
"Type": 1,
"Code": "equal 1"
}
],
Command: "mouse_tracking disable"
},
{
VarFloats: [
{
"Name": "login",
"Type": 1,
"Code": "equal 1"
},
{
"Name": "mouse",
"Type": 1,
"Code": "lower 1"
}
],
Command: "mouse_tracking enable",
File: ""
},
{
VarFloats: [
{
"Name": "login",
"Type": 1,
"Code": "equal 1"
},
{
"Name": "mouse",
"Type": 1,
"Code": "equal 1"
}
],
Command: "mouse_tracking disable",
File: ""
}
]
let motionToWav = {
"login_morning": {
Rexg: /MORNING/,
TimeLimit: {
"Hour": 5,
"Minute": 0,
"Sustain": 420
},
},
"login_afternoon": {
Rexg: /AFTERNOON/,
"TimeLimit": {
"Hour": 12,
"Minute": 0,
"Sustain": 420
},
},
"login_evening": {
Rexg: /EVENING/,
"TimeLimit": {
"Hour": 19,
"Minute": 0,
"Sustain": 300
},
},
"login_night": {
Rexg: /NIGHT/,
"TimeLimit": {
"Hour": 0,
"Minute": 0,
"Sustain": 300
},
},
"login_morning": {
Rexg: /MORNING/,
TimeLimit: {
"Hour": 5,
"Minute": 0,
"Sustain": 420
},
},
"login_morning": {
Rexg: /MORNING/,
TimeLimit: {
"Hour": 5,
"Minute": 0,
"Sustain": 420
},
},
"login_1": {
Rexg: /MORNING/,
TimeLimit: {
"Hour": 5,
"Minute": 0,
"Sustain": 420
},
},
"login_2": {
Rexg: /AFTERNOON/,
"TimeLimit": {
"Hour": 12,
"Minute": 0,
"Sustain": 420
},
},
"login_3": {
Rexg: /EVENING/,
"TimeLimit": {
"Hour": 19,
"Minute": 0,
"Sustain": 300
},
},
"login_4": {
Rexg: /NIGHT/,
"TimeLimit": {
"Hour": 0,
"Minute": 0,
"Sustain": 300
},
}
}
const HitAreas = [
]
let motionMenu = [
{
"Name": "动作欣赏",
"Text": "动作欣赏",
"Choices": [
],
"TextDuration": 5000
}
]
let controlloer = [
{
"Name": "控制面板",
"Text": "控制面板",
"Choices": [
{
"Text": "打开登录动画",
"NextMtn": "控制:打开登录动画"
},
{
"Text": "关闭登录动画",
"NextMtn": "控制:关闭登录动画"
},
{
"Text": "打开鼠标跟随",
"NextMtn": "控制:打开鼠标跟随"
},
{
"Text": "关闭鼠标跟随",
"NextMtn": "控制:关闭鼠标跟随"
},
{
"Text": "动作欣赏",
"NextMtn": "动作欣赏:动作欣赏"
}
],
"TextDuration": 5000
}
]
let loginAndMouseMenu = [
{
"Name": "打开登录动画",
"VarFloats": [
{
"Name": "login",
"Type": 2,
"Code": "assign 0"
}
]
},
{
"Name": "关闭登录动画",
"VarFloats": [
{
"Name": "login",
"Type": 2,
"Code": "assign 1"
}
]
},
{
"Name": "打开鼠标跟随",
"Command": "mouse_tracking enable",
"VarFloats": [
{
"Name": "mouse",
"Type": 2,
"Code": "assign 0"
}
]
},
{
"Name": "关闭鼠标跟随",
"Command": "mouse_tracking disable",
"VarFloats": [
{
"Name": "mouse",
"Type": 2,
"Code": "assign 1"
}
]
}
]
let KeyTrigger = {
"Items": [
{
"Input": 117,
"DownMtn": "控制面板:控制面板"
},
{
"Input": 118,
"DownMtn": "动作欣赏:动作欣赏"
}
],
"Enabled": true
}
let changeCosMenu = []
let wavFilePathList = []
let nowWavFilePathList = []
let nowMotionFilePathList = []
let allCharacterList = []
function findNowMotionJson(dirPath) {
const files = fs.readdirSync(dirPath);
for (const file of files) {
const filePath = path.join(dirPath, file);
const stat = fs.statSync(filePath);
if (stat.isDirectory()) {
findNowMotionJson(filePath);
} else if (filePath.endsWith(".motion3.json")) {
nowMotionFilePathList.push(filePath)
}
}
}
function mkdirSure(destDir) {
if (!fs.existsSync(destDir)) {
fs.mkdirSync(destDir, { recursive: true });
}
}
function findWav(dirPath) {
const files = fs.readdirSync(dirPath);
for (const file of files) {
const filePath = path.join(dirPath, file);
const stat = fs.statSync(filePath);
if (stat.isDirectory()) {
findWav(filePath);
} else if (filePath.endsWith(".wav") || filePath.endsWith(".ogg") || filePath.endsWith(".mp3")) {
wavFilePathList.push(filePath)
}
}
}
function getNowCharacterWav(characterName) {
let name = characterName.split("_")[0]
var nowCharacterWavRexg = new RegExp(name);
nowWavFilePathList.length = 0
for (let i = 0; i < wavFilePathList.length; i++) {
if (nowCharacterWavRexg.test(wavFilePathList[i])) {
nowWavFilePathList.push(wavFilePathList[i])
}
}
}
function copeFile(sourceFilePath, destinationFolderPath) {
fs.copyFileSync(sourceFilePath, destinationFolderPath);
}
function transFile(model3Json, characterName, dirPath) {
mkdirSure(path.dirname(path.join(outPath, path.join(characterName, model3Json.FileReferences.Moc))))
copeFile(path.join(dirPath, model3Json.FileReferences.Moc),
path.join(outPath, path.join(characterName, model3Json.FileReferences.Moc)))
model3Json.FileReferences.Moc =
path.join(characterName, model3Json.FileReferences.Moc)
for (let i = 0; i < model3Json.FileReferences.Textures.length; i++) {
mkdirSure(path.dirname(path.join(outPath, path.join(characterName, model3Json.FileReferences.Textures[i]))))
copeFile(path.join(dirPath, model3Json.FileReferences.Textures[i]),
path.join(outPath, path.join(characterName, model3Json.FileReferences.Textures[i])))
model3Json.FileReferences.Textures[i] =
path.join(characterName, model3Json.FileReferences.Textures[i])
}
if (model3Json.FileReferences.Physics) {
mkdirSure(path.dirname(path.join(outPath, path.join(characterName, model3Json.FileReferences.Physics))))
copeFile(path.join(dirPath, model3Json.FileReferences.Physics),
path.join(outPath, path.join(characterName, model3Json.FileReferences.Physics)))
model3Json.FileReferences.Physics =
path.join(characterName, model3Json.FileReferences.Physics)
}
if (model3Json.FileReferences.PhysicsV2) {
model3Json.FileReferences.PhysicsV2.File == model3Json.FileReferences.Physics
}
}
function handleMotion(model3Json, characterName, dirPath) {
getNowCharacterWav(characterName)
motionMenu.length = 1
motionMenu[0].Choices.length = 0
findNowMotionJson(dirPath)
let loginMotionNum = 0
if(!model3Json.FileReferences.Motions) {
model3Json.FileReferences.Motions = {}
}
model3Json.FileReferences.Motions["Start"] = []
model3Json.FileReferences.Motions["Idle"] = []
for (let i = 0; i < nowMotionFilePathList.length; i++) {
if (path.basename(nowMotionFilePathList[i]).startsWith("login")) {
loginMotionNum++
}
}
for (let i = 0; i < nowMotionFilePathList.length; i++) {
let motionName = path.basename(nowMotionFilePathList[i], ".motion3.json")
let newMotionpath = path.join(path.join(outPath, characterName), path.join("Motions", path.basename(nowMotionFilePathList[i])))
mkdirSure(path.dirname(newMotionpath))
copeFile(nowMotionFilePathList[i], newMotionpath)
if ((motionName.startsWith("normal") && !motionName.startsWith("normal_B") && !motionName.startsWith("normalB")) || motionName.startsWith("idle")) {
model3Json.FileReferences.Motions["Idle"].push(
{
"Name": motionName,
"File": path.join(characterName, path.join("Motions", path.basename(nowMotionFilePathList[i]))),
"Interruptable": true
}
)
continue
}
if (motionToWav[motionName]) {
for (let j = 0; j < nowWavFilePathList.length; j++) {
if (motionToWav[motionName].Rexg.test(nowWavFilePathList[j])) {
let newWavpath = path.join(path.join(outPath, characterName), path.join("Sounds", path.basename(nowWavFilePathList[j])))
mkdirSure(path.dirname(newWavpath))
copeFile(nowWavFilePathList[j], newWavpath)
if (motionName.startsWith("login")) {
for (let z = 0; z < loginAndMouse.length; z++) {
model3Json.FileReferences.Motions["Start"].push({
"Name": motionName,
"Sound": loginAndMouse[z].File ?? path.join(characterName, path.join("Sounds", path.basename(nowWavFilePathList[j]))),
"File": loginAndMouse[z].File ?? path.join(characterName, path.join("Motions", path.basename(nowMotionFilePathList[i]))),
"Text": "",
"Command": loginAndMouse[z].Command,
"TimeLimit": loginMotionNum > 3 ? motionToWav[motionName].TimeLimit : null,
"VarFloats": loginAndMouse[z].VarFloats
})
}
}
}
}
}
else {
const randomNum = Math.floor(Math.random() * nowWavFilePathList.length)
let newWavpath = path.join(path.join(outPath, characterName), path.join("Sounds", path.basename(nowWavFilePathList[randomNum])))
mkdirSure(path.dirname(newWavpath))
copeFile(nowWavFilePathList[randomNum], newWavpath)
motionMenu.push({
"Name": motionName,
"File": path.join(characterName, path.join("Motions", path.basename(nowMotionFilePathList[i]))),
"Sound": path.join(characterName, path.join("Sounds", path.basename(nowWavFilePathList[randomNum]))),
"Text": "",
"Interruptable": true
})
motionMenu[0].Choices.push({
"Text": motionName,
"NextMtn": `动作欣赏:${motionName}`
})
}
}
if (model3Json.FileReferences.Motions["Start"].length === 0) {
for (let z = 0; z < loginAndMouse.length; z++) {
model3Json.FileReferences.Motions["Start"].push({
"Name": z,
"Text": "",
"Command": loginAndMouse[z].Command,
"VarFloats": loginAndMouse[z].VarFloats
})
}
}
if (model3Json.FileReferences.Motions["Idle"].length === 0) {
model3Json.FileReferences.Motions["Idle"].length === null
}
model3Json.FileReferences.Motions[""] = null
}
function handleCos(model3Json, characterName, dirPath) {
model3Json.FileReferences.Motions["控制面板"] = controlloer
model3Json.FileReferences.Motions["Tap换装"] = changeCosMenu
model3Json.HitAreas = HitAreas
model3Json.FileReferences.Motions["动作欣赏"] = motionMenu
model3Json.FileReferences.Motions["控制"] = loginAndMouseMenu
}
function getCharacterName(filePath) {
const match = filePath.match(characterNameRexg);
let characterName
if (match) {
characterName = match[1];
} else {
console.log('提取角色名称错误');
}
return characterName
}
function hanleKeyTrigger(model3Json, characterName, dirPath) {
if(!model3Json.Controllers) {
model3Json.Controllers = {}
}
model3Json.Controllers.KeyTrigger = KeyTrigger
}
function findModelJSON(dirPath) {
const files = fs.readdirSync(dirPath);
for (const file of files) {
const filePath = path.join(dirPath, file);
const stat = fs.statSync(filePath);
if (stat.isDirectory()) {
findModelJSON(filePath);
} else if (filePath.endsWith(".model3.json")) {
let characterName
try {
let model3Json = JSON.parse(fs.readFileSync(filePath, 'utf8'))
characterName = getCharacterName(filePath)
if (!characterName) {
return
}
nowMotionFilePathList.length = 0
transFile(model3Json, characterName, dirPath)
handleMotion(model3Json, characterName, dirPath)
handleCos(model3Json, characterName, dirPath)
hanleKeyTrigger(model3Json, characterName, dirPath)
fs.writeFileSync(path.join(outPath, `${characterName}.model3.json`), JSON.stringify(model3Json, '\t'))
console.log(`${characterName}已处理`);
}catch(e) {
console.log(e);
console.log(`${characterName}转换出错`);
}
}
}
}
function findAllCharacter(dirPath) {
const files = fs.readdirSync(dirPath);
for (const file of files) {
const filePath = path.join(dirPath, file);
const stat = fs.statSync(filePath);
if (stat.isDirectory()) {
findAllCharacter(filePath);
} else if (filePath.endsWith(".model3.json")) {
let characterName = getCharacterName(filePath)
if (characterName) {
allCharacterList.push(characterName)
}
}
}
}
function getChangeCharacterMenu() {
const cen = Math.ceil(allCharacterList.length / 20)
console.log(JSON.stringify(allCharacterList));
let index = 0
for (let i = 0; i < cen; i++) {
let changeCosI = {
"Name": `换装${i}`,
"Text": `换装${i}`,
"Choices": [
],
"TextDuration": 5000
}
changeCosMenu.push(changeCosI)
controlloer[0].Choices.push({
"Text": `换装${i}`,
"NextMtn": `Tap换装:换装${i}`
})
for (let j = 0; j < 20 && index < allCharacterList.length; index++, j++) {
changeCosMenu.push({
"Name": allCharacterList[index],
"Command": `change_cos ${allCharacterList[index]}.model3.json`
})
changeCosI.Choices.push({
"Text": allCharacterList[index],
"NextMtn": `Tap换装:${allCharacterList[index]}`
})
}
}
}
function init() {
findWav(wavPath)
findAllCharacter(filePath)
getChangeCharacterMenu()
findModelJSON(filePath)
}
init()