じ☆ve冰风 发表于 2025-10-3 22:20:50

Rmmv实现脸图索引显示对应角色名字插件

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

JAVASCRIPT 代码下载

/*:
* @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一样,范围: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;
    }
    // =================
    // 配置所有可能用到的皮肤图片文件(文件必须存在)
    // =========
    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){
                case'original':
                  currentMode = 0;
                  break;
                case'reset':
                  currentMode = 1;
                  break;
                case'selftName':
                  currentMode = 2;
                  selftName = args;
                  break;
                case'selfEid':
                  currentMode = 3;
                  currenEid = Number(args);
                  break;
                case'wLeft':
                  WindowsPosition = 0;
                  break;
                case'wCenter':
                  WindowsPosition = 1;
                  break;
                case'wRight':
                  WindowsPosition = 2;
                  break;
                case'msgNameColor':
                  MsgNameColorId = Number(args);
                  break;
                case'Skin':
                  const newSkin = args;
                  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){
            returnfalse;
      }
    }
    // 初始化配置文件(如果不存在则初始化配置)
    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'
                );
                // 成功
                returntrue;
            }catch(e){
                // 创建默认配置文件失败
                returnfalse;
            }
      }
      returntrue;
    }

    function saveWindowSkin(skinName){
      // 确保配置文件存在
      if(!initializeConfigFile()){returnfalse; }
      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'
            );
            // 已保存皮肤设置
            returntrue;
      }catch(e){
            console.error('保存皮肤设置失败:', e);
            returnfalse;
      }
    }
    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}, ${tone}, ${tone},0.75)`;
    };
})();

            本帖来自P1论坛作者codePlayerD,因Project1站服务器在国外有时候访问缓慢不方便作者交流学习,经联系P1站长fux2同意署名转载一起分享游戏制作经验,共同为国内独立游戏作者共同创造良好交流环境,原文地址:https://rpg.blue/forum.php?mod=viewthread&tid=498154若有侵权,发帖作者可联系底部站长QQ在线咨询功能删除,谢谢。
页: [1]
查看完整版本: Rmmv实现脸图索引显示对应角色名字插件