查看: 1580|回复: 2

[原创发布] Rmmv实现脸图索引显示对应角色名字插件

[复制链接]
梦石
0
星屑
86
在线时间
8 小时
注册时间
2025-6-14
回帖
3
发表于 2025-10-3 22:32:45 | 显示全部楼层 |阅读模式

加入我们,或者,欢迎回来。

您需要 登录 才可以下载或查看,没有账号?注册会员

×
插件实现了以下功能:
1-通过提前配置脸图对应角色姓名,在写对话的时候直接在名字窗口上面直接显示角色姓名;
2-通过插件实现区间内所有对话都使用插件指令获得的姓名
更多功能已经写现在插件里面了
对话框的换肤实现了保存到配置,可以使用插件指令搭配对话选项进行简单的对话框换肤

[pre lang="javascript" line="0" file="OYS_TiMessage"]/*:
* @plugindesc 简单增强对话框1.2.1
* @author OYS_codePlayerD(缘系列)
* @version 1.2.1
*
* @param TiMsgModel
* @text 开启脸图索引名字
* @desc 开启后通过脸图和索引设置对话e的角色名字
* @type boolean
* @default true
*
* @param PlayerFaceNames
* @text 指定特殊角色脸图文件名
* @desc 使用逗号分隔,用于从数据文件获得对应角色数据
* @default Actor1,Actor2
*
* @param setMsgNameColor
* @text 指定名字框文字颜色
* @desc 设置名字框文字初始颜色,数值:0-31
* @type number
* @min 0
* @max 31
* @default 2
*
* @param msgNameFontSize
* @text 名字框名字大小
* @desc 设置名字框名字大小
* @type number
* @default 28
*
* @param nameWindowHeight
* @text 名字框的最大高度
* @desc 设置名字框的最大高度
* @type number
* @default 100
*
* @param PositionTypeX
* @text 起始窗口位置
* @desc 起始时窗口位置,后续可通过插件指令更改
* @type select
* @option 左方
* @value 2
* @option 中间
* @value 1
* @option 方右
* @value 0
* @default 0
*
* @param defaultOffsetY
* @text 名字框相对窗口Y偏移
* @desc Y:以对话框为参照点,向上偏移的数值
* @type number
* @default 0
*
* @help
*  插件功能:
*      1-增加对话角色名字框
*      2-实现了几种更换对话角色名字的功能
*      3-实现对话窗口相关换肤功能(支持保存到配置文件)
*  名字相关插件命令:
*      TiMsg original
*            :0-原始模式(没有名字窗口那个)
*      TiMsg selftName 我是名字
*            :2-自定义名字
*      TiMsg selfEid 1
*           :3-通过Id获得这个id对应的名称
*      TiMsg reset
*           :1-重置,与上述3个指令组合成区间效果
*           举例:
*               TiMsg selfEid 1
*                |
*                |区间内所有对话名字使用id=1这个事件的名称,以上其余同理
*                |
*               TiMsg reset
*      TiMsg msgNameColor ColorId  
*            : 这里是切换对话角色名字的颜色
*          - 示例 : TiMsg msgNameColor 2 :和\c[2]一样,范围:0-31
*  名字框插件相关指令:
*      TiMsg wLeft   :名字框靠左
*      TiMsg wCenter :名字框居中
*      TiMsg wRight  :名字框靠右
*  对话窗口皮肤相关插件指令:
*      TiMsg Skin fileName
*            说明:实现切换小窗口的皮肤切换,可以再对话时添加指令
*            注意:请务必预加载皮肤,不然可能会使用默认皮肤或直接没有皮肤
*      TiMsg saveIniSkin
*          --保存对话皮肤配置
* 版权备注:
*      1-免费可商用,但请注明作者:OYS
*
*/

(function () {
    const pluginName = 'OYS_TiMessage';
    const parameters = PluginManager.parameters(pluginName);
    const playerFaceNames = (parameters['PlayerFaceNames'] || '').split(',').map(name => name.trim());
    // 窗口相关参数,这是为了时限对话角色名字
    let defaultWidth = 200;
    let defaultHeight = Number(parameters['nameWindowHeight'] || 100);
    let defaultX = 50;
    let defaultY = Number(parameters['defaultOffsetY'] || 0);
    //=================分割========
    let TiMsgModel = (parameters['TiMsgModel'] || true);
    let MsgNameColorId = Number(parameters['setMsgNameColor'] || 2);
    let currentMode = 1;
    let selftName = "";
    let currenEid = 0;
    let WindowsPosition = 0;
    // 引入NW.js的文件系统和路径模块,实现对话窗口换肤配置
    const fs = require('fs');
    const path = require('path');

    // 在这里配置脸图对应的数据
    function actorsDataGet(currentFaceName, faceIndex) {
        // 为每张图片绑定对应角色名
        var actorsData = {
            "Actor1": {
                "0": getActorsName(1),
                "1": getActorsName(2),
                "2": getActorsName(3),
                "3": getActorsName(4),
                "4": getActorsName(1),
                "5": "自定义",
                "6": getActorsName(1),
                "7": getActorsName(1),
            },
            "Actor2": {
                "0": getActorsName(1),
                "1": getActorsName(1),
                "2": getActorsName(2),
                "3": getActorsName(4),
                "4": getActorsName(2),
                "5": getActorsName(3),
                "6": getActorsName(4),
                "7": "自定义名字",
            },
        };
        return actorsData[currentFaceName][faceIndex];
    }
    // =================
    // 配置所有可能用到的皮肤图片文件(文件必须存在)
    // =========
    const availableSkins = [
        // 这个是窗口皮肤配置,要用的皮肤文件都写在这里
        // 这是给名字窗口使用的皮肤
        'Window',
        'Window2',
    ];
    availableSkins.forEach(skinName => {
        // 预加载皮肤,如果不做预加载,小窗口皮肤无法使用哦
        ImageManager.loadSystem(skinName);
    });


    // ========皮肤,从文件中加载============
    // 因为不干扰存档文件,所以配置文件单开,
    // 所以保存需要使用插件指令
    // =============================
    let currentSkin = loadWindowSkin(); // 打开游戏时就加载配置
    let msgNameFontSize = Number(parameters['msgNameFontSize'] || 28);
    const _Game_Interpreter_pluginCommand = Game_Interpreter.prototype.pluginCommand;
    Game_Interpreter.prototype.pluginCommand = function (command, args) {
        _Game_Interpreter_pluginCommand.call(this, command, args);
        if (command === 'TiMsg') {
            switch (args[0]) {
                case 'original':
                    currentMode = 0;
                    break;
                case 'reset':
                    currentMode = 1;
                    break;
                case 'selftName':
                    currentMode = 2;
                    selftName = args[1];
                    break;
                case 'selfEid':
                    currentMode = 3;
                    currenEid = Number(args[1]);
                    break;
                case 'wLeft':
                    WindowsPosition = 0;
                    break;
                case 'wCenter':
                    WindowsPosition = 1;
                    break;
                case 'wRight':
                    WindowsPosition = 2;
                    break;
                case 'msgNameColor':
                    MsgNameColorId = Number(args[1]);
                    break;
                case 'Skin':
                    const newSkin = args[1];
                    currentSkin = availableSkins.includes(newSkin) ? newSkin : 'Window';
                    break;
                case 'saveIniSkin':
                    saveWindowSkin(currentSkin)
                    break;
            }
        }
    };

    // ============================
    // 这里操作配置文件
    // --实现 对话窗口皮肤 的配置
    // --实现 选项窗口皮肤 的配置
    // --可拓展 实现 菜单窗口皮肤 的配置
    // =====================
    // 获取配置文件
    function getConfigFilePath() {
        return path.join(process.cwd(), 'wini.json');
    }
    function doesConfigFileExist() {
        try {
            return fs.existsSync(getConfigFilePath()) &&
                fs.statSync(getConfigFilePath()).isFile();
        } catch (e) {
            return false;
        }
    }
    // 初始化配置文件(如果不存在则初始化配置)
    function initializeConfigFile() {
        if (!doesConfigFileExist()) {
            try {
                const defaultConfig = {
                    windowSkin: 'Window',//窗口,给window_Base用
                    WindowMessageSkin: 'Window2',//对话窗口皮肤
                    lastSaved: new Date().toISOString()
                };
                fs.writeFileSync(
                    getConfigFilePath(),
                    JSON.stringify(defaultConfig, null, 2),
                    'utf8'
                );
                // 成功
                return true;
            } catch (e) {
                // 创建默认配置文件失败
                return false;
            }
        }
        return true;
    }

    function saveWindowSkin(skinName) {
        // 确保配置文件存在
        if (!initializeConfigFile()) { return false; }
        try {
            const currentConfig = JSON.parse(fs.readFileSync(getConfigFilePath(), 'utf8'));
            const updatedConfig = {
                ...currentConfig,
                WindowMessageSkin: skinName,
                lastSaved: new Date().toISOString()
            };
            fs.writeFileSync(
                getConfigFilePath(),
                JSON.stringify(updatedConfig, null, 2),
                'utf8'
            );
            // 已保存皮肤设置
            return true;
        } catch (e) {
            console.error('保存皮肤设置失败:', e);
            return false;
        }
    }
    function loadWindowSkin() {
        // 确保配置文件存在
        if (!initializeConfigFile()) { return 'Window'; }
        try {
            const config = JSON.parse(fs.readFileSync(getConfigFilePath(), 'utf8'));
            if (config && typeof config.WindowMessageSkin === 'string') {
                return config.WindowMessageSkin;
            } else {
                // 配置文件格式不正确,使用默认皮肤
                return 'Window';
            }
        } catch (e) {
            // 加载皮肤设置失败
            return 'Window';
        }
    }

    function getActorsName(id) {
        // 通过id获取角色数据库里的对应角色名
        return $gameActors.actor(id) ? $gameActors.actor(id).name() : "未知角色数据";
    }

    // =========
    // 实现设置姓名框的名字
    // ===========================
    const _Game_Message_allText = Game_Message.prototype.allText;
    Game_Message.prototype.allText = function () {
        const originalText = _Game_Message_allText.call(this);
        if (!originalText) return originalText;
        if (TiMsgModel) {
            const currentFaceName = this.faceName();
            const faceIndex = this.faceIndex();
            // 处理脸图加索引以及没有脸图
            if (currentMode === 1) {
                if (currentFaceName.trim() === '') {
                    // 使用事件名作为名称
                    const currentEvent = $gameMap._interpreter._eventId ?
                        $gameMap.event($gameMap._interpreter._eventId) : null;
                    const eventName = currentEvent ? currentEvent.event().name : "未知事件";
                    setMsgNameTxt(eventName);
                    return originalText;
                }
                // 使用 脸图+索引 模式
                const isActors = playerFaceNames.includes(currentFaceName);
                if (isActors) {
                    const msgName = actorsDataGet(currentFaceName, faceIndex);
                    setMsgNameTxt(msgName);
                    return originalText;
                } else {
                    const currentEvent = $gameMap._interpreter._eventId ?
                        $gameMap.event($gameMap._interpreter._eventId) : null;
                    const eventName = currentEvent ? currentEvent.event().name : "未知事件";
                    setMsgNameTxt(eventName);
                    return originalText;
                }
            }
            if (currentMode === 0) {
                // 原始的模式
                setMsgNameTxt(selftName);
                return originalText;
            }
            if (currentMode === 2) {//自定义名字
                setMsgNameTxt(selftName);
                return originalText;
            }
            if (currentMode === 3) {
                // 事件名称
                if (currenEid < 1) {
                    const currentEvent = $gameMap._interpreter._eventId ?
                        $gameMap.event($gameMap._interpreter._eventId) : null;
                    const eventName = currentEvent ? currentEvent.event().name : "未知事件";
                    setMsgNameTxt(eventName);
                    return originalText;
                } else {
                    const eventNameEid = $gameMap.event(currenEid).event().name;
                    setMsgNameTxt(eventNameEid);
                    return originalText;
                }
            }
        }
        return originalText;
    };
    function setMsgNameTxt(txt) {
        selftName = txt;
    }

    //===============
    //名字窗口,原本想调用窗口来实现,但发现不符合预期
    //=====================================
    var _Window_Message_initialize = Window_Message.prototype.initialize;
    Window_Message.prototype.initialize = function () {
        _Window_Message_initialize.call(this);
        this.createSkinWindow();
        this.createMsgNameTxt();
        this.windowskin = ImageManager.loadSystem(currentSkin)
    };
    Window_Message.prototype.createSkinWindow = function () {
        //模拟实现窗口皮肤效果
        this._MsgWindowSkin = new Sprite(new Bitmap(defaultWidth + 16, defaultHeight + 16));
        this._MsgWindowSkin.x = defaultX;
        this._MsgWindowSkin.y = (defaultY + defaultHeight + 16) * (-1);
        this._MsgWindowSkin.visible = true;
        this.addChild(this._MsgWindowSkin);
        if (!this._MsgWindowSkin) return;
        this.skin = ImageManager.loadSystem(currentSkin);
        if (!this.skin.isReady()) {// 加载失败则降级到默认皮肤
            this.skin = ImageManager.loadSystem('Window');
        } else {
            const bitmap = this._MsgWindowSkin.bitmap;
            const width = bitmap.width;
            const height = bitmap.height;
            bitmap.clear();
            const padding = 3.7;
            const w = width - padding * 2;
            const h = height - padding * 2;
            this.drawBackgroud(bitmap, this.skin, padding, 0, 0, w, h);
            const mw = 8; // 边框宽度
            this.drawBord(bitmap, this.skin, 1, 0, mw, width, height)
        }
    };
    Window_Message.prototype.updateWindowSkin = function (txtWidth) {
        const getChangesta = txtWidth > defaultWidth;
        let setSkinWinW = getChangesta ? txtWidth : defaultWidth;
        this._MsgWindowSkin.bitmap = new Bitmap(setSkinWinW + 16 - 3.7, defaultHeight + 16);
        const bitmap = this._MsgWindowSkin.bitmap;
        const width = bitmap.width;
        const height = bitmap.height;
        bitmap.clear();
        const nmaePadding = 8;
        // 这里实现变换位置
        if (WindowsPosition === 0) {
            this._MsgWindowSkin.x = 10 - nmaePadding;
        }
        if (WindowsPosition === 1) {
            this._MsgWindowSkin.x = (Graphics.boxWidth - setSkinWinW) / 2 - nmaePadding;
        }
        if (WindowsPosition === 2) {
            this._MsgWindowSkin.x = (Graphics.boxWidth - setSkinWinW) - nmaePadding;
        }
        // 这里实现更换皮肤
        this.skin = ImageManager.loadSystem(currentSkin);
        this.windowskin = ImageManager.loadSystem(currentSkin)
        const padding = 3.7;
        const w = width - padding * 2;
        const h = height - padding * 2;
        this.drawBackgroud(bitmap, this.skin, padding, 0, 0, w, h);
        const mw = 8; // 边框宽度
        this.drawBord(bitmap, this.skin, 1, 0, mw, width, height)
    }

    Window_Message.prototype.createMsgNameTxt = function () {
        this._msgNameTxtSprite = new Sprite();
        this._msgNameTxtSprite.bitmap = new Bitmap(defaultHeight, defaultHeight);
        this._msgNameTxtSprite.bitmap.fontSize = Window_Base.prototype.standardFontSize();
        // 这里初始计算是以对话窗口左上角为0,0,
        // 在这里还是蛮好用的,刚好设置到窗口上方
        this._msgNameTxtSprite.x = defaultX + 8;
        this._msgNameTxtSprite.y = (defaultY + defaultHeight + 8) * (-1);
        this._msgNameTxtSprite.visible = false;
        this.z = 5;
        this.addChild(this._msgNameTxtSprite);
    };
    Window_Message.prototype.updateMsgNameTxt = function (txtWidth) {
        const getTxtChangeSta = txtWidth > defaultWidth;
        let TxtWidthSet = getTxtChangeSta ? txtWidth : defaultWidth;
        this._msgNameTxtSprite.visible = true;
        this._msgNameTxtSprite.bitmap = new Bitmap(TxtWidthSet - 4, defaultHeight);
        this._msgNameTxtSprite.bitmap.clear();
        // 这里实现变换位置
        if (WindowsPosition === 0) {
            this._msgNameTxtSprite.x = 10;
        }
        if (WindowsPosition === 1) {
            this._msgNameTxtSprite.x = (Graphics.boxWidth - TxtWidthSet) / 2;
        }
        if (WindowsPosition === 2) {
            this._msgNameTxtSprite.x = (Graphics.boxWidth - TxtWidthSet);
        }
        // 更换字体大小
        this._msgNameTxtSprite.bitmap.fontSize = msgNameFontSize;
        this._msgNameTxtSprite.bitmap.textColor = this.textColor(MsgNameColorId);
        this._msgNameTxtSprite.bitmap.drawText(selftName, 0, 0, TxtWidthSet - 4, defaultHeight, 'center');
    }
    Window_Message.prototype.updateCustomSprite = function () {
        let txtWidth = this._msgNameFontSize * selftName.length;
        if (this._MsgWindowSkin) {
            this._MsgWindowSkin.visible = false;
            if (currentMode !== 0) {
                this._MsgWindowSkin.visible = true;
                this.updateWindowSkin(txtWidth);
            }
        }
        if (this._msgNameTxtSprite) {
            this._msgNameTxtSprite.visible = false;
            if (currentMode !== 0) {
                this.updateMsgNameTxt(txtWidth);
            }
        }
    }

    var _Window_Message_prototype_update = Window_Message.prototype.update;
    Window_Message.prototype.update = function () {
        _Window_Message_prototype_update.call(this);
        //留着作为特效拓展,暂时没想好
    }
    var _Window_Message_startMessage = Window_Message.prototype.startMessage;
    Window_Message.prototype.startMessage = function () {
        _Window_Message_startMessage.call(this);
        this.updateCustomSprite();
    };
    var _Window_Message_terminateMessage = Window_Message.prototype.terminateMessage;
    Window_Message.prototype.terminateMessage = function () {
        _Window_Message_terminateMessage.call(this);
        if (this._msgNameTxtSprite) {
            this._msgNameTxtSprite.visible = false;
        }
        if (this._MsgWindowSkin) {
            this._MsgWindowSkin.visible = false;
        }
    };
    // =========
    // 对话选项换皮肤
    // ==================
    var Window_ChoiceList_prototype_start = Window_ChoiceList.prototype.start;
    Window_ChoiceList.prototype.start = function () {
        Window_ChoiceList_prototype_start.call(this);
        this.windowskin = ImageManager.loadSystem(currentSkin);
    };
    //=====================
    // 这里是处理小窗口的绘制,基本保持不动就好了
    // =======================================
    Window_Message.prototype.drawBord = function (tempBitmapBorder, skin, cpX, cpY, mw, width, height) {
        //对比用,心累填数据: bitmap.blt(源图片, 源x, 源y, 源宽, 源高, 目标x, 目标y, 目标宽, 目标高);
        // 左边两角
        tempBitmapBorder.blt(skin, cpX * 96, 0, mw, mw, 0, 0, mw, mw); // 左上角
        tempBitmapBorder.blt(skin, cpX * 96, 96 + 96 * cpY - mw, mw, mw, 0, height - mw, mw, mw); // 左下角
        // 右边两角
        tempBitmapBorder.blt(skin, 96 + cpX * 96 - mw, 0, mw, mw, width - mw, 0, mw, mw); // 右上角
        tempBitmapBorder.blt(skin, 96 + cpX * 96 - mw, 96 + cpY * 96 - mw, mw, mw, width - mw, height - mw, mw, mw); // 右下角
        // 上下边线
        tempBitmapBorder.blt(skin, cpX * 96 + mw, 0, 96 - 2 * mw, mw, mw, 0, width - 2 * mw, mw); // 上边
        tempBitmapBorder.blt(skin, cpX * 96 + mw, 96 - mw, 96 - 2 * mw, mw, mw, height - mw, width - 2 * mw, mw);
        // 左右边线
        tempBitmapBorder.blt(skin, cpX * 96, mw, mw, 96 - 2 * mw, 0, mw, mw, height - 2 * mw); // 左边
        tempBitmapBorder.blt(skin, 96 + cpX * 96 - mw, mw, mw, 96 - 2 * mw, width - mw, mw, mw, height - 2 * mw);
    };

    Window_Message.prototype.drawBackgroud = function (drawBackgroudBitmap, skin, padding, cpX, cpY, w, h) {
        const mw = 96;
        const backColor = this.contentsBackColor();
        drawBackgroudBitmap.clear();


        drawBackgroudBitmap.context.globalCompositeOperation = 'overlay';
        drawBackgroudBitmap.context.globalAlpha = 0.75;
        // 绘制窗口平铺花纹
        const sx = 0;
        const sy = 96;
        const sw = mw - 0 * 2;
        const sh = mw - 0 * 2;
        for (let y = padding; y < h + padding; y += sh) {
            for (let x = padding; x < w + padding; x += sw) {
                const drawWidth = Math.min(sw, w + padding - x);
                const drawHeight = Math.min(sh, h + padding - y);
                drawBackgroudBitmap.blt(skin, sx, sy, sw, sh, x, y, drawWidth, drawHeight);
            }
        }
        drawBackgroudBitmap.fillRect(padding, padding, w, h, backColor);
        drawBackgroudBitmap.context.globalAlpha = 1;
        drawBackgroudBitmap.context.globalCompositeOperation = 'source-over';

    };
    // 模拟RMMV的背景色获取
    Window_Message.prototype.contentsBackColor = function () {
        const tone = $gameSystem.windowTone();
        return `rgba(${tone[0]}, ${tone[1]}, ${tone[2]},0.75)`;
    };
})();
[/pre]
梦石
0
星屑
3633
在线时间
45 小时
注册时间
2025-8-14
回帖
31
发表于 2025-10-4 16:55:39 | 显示全部楼层
用了之后显示的只字母+数字? 怎么配置显示名字?
// 在这里配置脸图对应的数据
    function actorsDataGet(currentFaceName, faceIndex) {
        // 为每张图片绑定对应角色名
        var actorsData = {
            "A01": {
                "0": getActorsName(1),
                "1": getActorsName(2),
                "2": getActorsName(3),
                "3": getActorsName(4),
                "4": getActorsName(1),
                "5": "自定义",
                "6": getActorsName(1),
                "7": getActorsName(1),
            },
            "Actor2": {
                "0": getActorsName(1),
                "1": getActorsName(1),
                "2": getActorsName(2),
                "3": getActorsName(4),
                "4": getActorsName(2),
                "5": getActorsName(3),
                "6": getActorsName(4),
                "7": "自定义名字",
            },
        };
        return actorsData[currentFaceName][faceIndex];
在这边改括号里面1吗?  改成名字也不管用
回复

使用道具 举报

梦石
0
星屑
3633
在线时间
45 小时
注册时间
2025-8-14
回帖
31
发表于 2025-10-4 17:10:13 | 显示全部楼层
        // 为每张图片绑定对应角色名
        var actorsData = {
            "A01": {
            "0": "洛卡斯",  // 索引0 → 洛卡斯
            "1": "洛卡斯",  // 索引1 → 洛卡斯
            "2": "洛卡斯",  // 索引2 → 洛卡斯
            "3": "洛卡斯",  // 索引3 → 洛卡斯
            "4": "洛卡斯",  // 索引4 → 洛卡斯
            "5": "洛卡斯",  // 索引5 → 洛卡斯
            "6": "洛卡斯",  // 索引6 → 洛卡斯
            "7": "洛卡斯"   // 索引7 → 洛卡斯
            },
            "A02": {
            "0": "洛薇尔",  // 索引0 → 洛卡斯
            "1": "洛薇尔",  // 索引1 → 洛卡斯
            "2": "洛薇尔",  // 索引2 → 洛卡斯
            "3": "洛薇尔",  // 索引3 → 洛卡斯
            "4": "洛薇尔",  // 索引4 → 洛卡斯
            "5": "洛薇尔",  // 索引5 → 洛卡斯
            "6": "洛薇尔",  // 索引6 → 洛卡斯
            "7": "洛薇尔"   // 索引7 → 洛卡斯
            },
        };   名字都改了也不行啊  还是显示事件名字
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

Powered by Discuz! X5.0 © 2001-2026 Discuz! Team.

在本版发帖返回顶部