// https://docs.google.com/spreadsheets/d/1-3E_t-KsFIw2rpM0hZIKOdNdGm5-GnQjvJb3cysOawE/edit#gid=1532331536 const fs = require("fs"); const _ = require("underscore"); const { _models, shuffledModels } = require("./models"); if (!fs.existsSync("config.js")) { fs.writeFileSync("config.js", fs.readFileSync("config.default.js")); } if (!fs.existsSync("output")) { fs.mkdirSync("output"); fs.writeFileSync( "output/character_character_data.bin", fs.readFileSync("original/character_character_data.bin") ); fs.writeFileSync( "output/character_model_model_data.bin", fs.readFileSync("original/character_model_model_data.bin") ); } const { mode, overwrite, sameType, singleModel, randomModels, exclusions, blacklist, valid, } = require("./config"); // Check if a model is excluded from modification function isModelExcluded(fb, sb) { if (!exclusions.enabled) return false; 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) { if (!blacklist.enabled) return false; 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 isValidLocation(loc) { if (!valid.enabled) return true; if (!valid.locations.length) return true; for (let i = 0; i < valid.locations.length; i++) { if (valid.locations[i] === loc) return valid.isWhitelist; } return !valid.isWhitelist; } function replaceModels(doReplace = true) { const data = fs.readFileSync( `${overwrite ? "output" : "original"}/character_character_data.bin` ); let nonValid = 0; let excluded = 0; let replaced = 0; if (doReplace) { // Magic if (mode === 0) { for (let i = 0x16194; i < 0x18ea8; i += 4) { if (!isValidLocation(i)) { nonValid++; continue; } 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 (!isValidLocation(i)) { nonValid++; continue; } 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) { if (!isValidLocation(i)) { nonValid++; continue; } 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} - Non-Valid ${nonValid}` ); 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( `${overwrite ? "output" : "original"}/character_model_model_data.bin` ); 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);