// https://docs.google.com/spreadsheets/d/1-3E_t-KsFIw2rpM0hZIKOdNdGm5-GnQjvJb3cysOawE/edit#gid=1532331536 const fs = require("fs"); const _ = require("underscore"); const { _models, allTypes, shuffledModels } = require("./models"); if (!fs.existsSync("config.js")) { fs.writeFileSync("config.js", fs.readFileSync("config.default.js")); } if (!fs.existsSync("output")) { fs.mkdirSync("output"); } const { mode, sameType, singleModel, randomModels, exclusions, blacklist, } = require("./config"); // Check if a model is excluded from modification function isModelExcluded(fb, sb) { for (let i = 0; i < exclusions.models.length; i++) { const model = exclusions.models[i]; if ( (model[0] === fb && model[1] === -1) || (model[0] === -1 && model[1] === sb) ) { return !exclusions.isWhitelist; } else if (model[0] === fb && model[1] === sb) { return !exclusions.isWhitelist; } } return exclusions.isWhitelist; } // Check if a model is blacklisted function isModelBlacklisted(fb, sb) { for (let i = 0; i < blacklist.models.length; i++) { const model = blacklist.models[i]; if ( (model[0] === fb && model[1] === -1) || (model[0] === -1 && model[1] === sb) ) { return !blacklist.isWhitelist; } else if (model[0] === fb && model[1] === sb) { return !blacklist.isWhitelist; } } return blacklist.isWhitelist; } function replaceModels(doReplace = true) { const data = fs.readFileSync("original/character_character_data.bin.orig"); let replaced = 0; let excluded = 0; if (doReplace) { // Magic if (mode === 0) { for (let i = 0x16194; i < 0x18ea8; i += 4) { if (isModelExcluded(data[i], data[i + 1])) { excluded++; continue; } data[i] = singleModel[0]; data[i + 1] = singleModel[1]; replaced++; } } else if (mode === 1) { for (let i = 0x16194; i < 0x18ea8; i += 4) { if (isModelExcluded(data[i], data[i + 1])) { excluded++; continue; } const model = randomModels[_.random(randomModels.length - 1)]; data[i] = model[0]; data[i + 1] = model[1]; replaced++; } } else if (mode === 2) { for (let i = 0x16194; i < 0x18ea8; i += 4) { const id = data[i]; const type = data[i + 1]; if (isModelExcluded(id, type)) { excluded++; continue; } if (sameType) { const models = shuffledModels[type]; let modelId = models[_.random(models.length - 1)]; while (isModelBlacklisted(modelId, type)) { modelId = models[_.random(models.length - 1)]; } data[i] = modelId; data[i + 1] = type; } else { let [modelId, modelType] = _models[_.random(_models.length - 1)]; while (isModelBlacklisted(modelId, modelType)) { [modelId, modelType] = _models[_.random(_models.length - 1)]; } data[i] = modelId; data[i + 1] = modelType; } replaced++; } } // ----- } console.log(`Replaced ${replaced} models - Excluded ${excluded}`); fs.writeFileSync("output/character_character_data.bin", data); } // TODO: Create a reverse-lookup map of some sort to fit the proper model height with other models? // Only does scaling, so not that needed apparently... function replaceHeights(doReplace = true) { const data = fs.readFileSync("original/character_model_model_data.bin.orig"); if (doReplace) { // Magic for (let i = 0x23220; i < 0x24910; i += 2) { data[i] = 0xb9; data[i + 1] = 0x00; } // ----- } fs.writeFileSync("output/character_model_model_data.bin", data); } replaceModels(); replaceHeights(false);