累计送礼: 0 个 累计收礼: 0 个 TA的每日心情 开心 2025-8-20 22:20
签到天数: 161 天
连续签到: 1 天
[LV.7]常住居民III
管理员
VIP
6
卡币
13957
OK点
16
推广点
0
同能卷
0
积分 17010
这是一份简单的物品合成插件。
实现的功能为:根据已有的配方文件和对应的操作台,如果物品栏中已有合成材料的种类和数量满足条件,则合成得到产品。例如A+B=C, A+B+C=D之类的。
其中配方文件是外置于插件的,默认路径为"data/SynthesisRecipe.json"; 而操作台类型的定义是写在插件内部的”const WORKSTATIONS ={...}“。使用者可以自行修改插件内的操作台类型定义,例如添加操作台等。注:当外部配方文件不存在或加载失败时,会加载内部定义的缺省配方”const SAMPLE_RECIPES = {{...},{...}}“;这份缺省配方的格式,可以作为书写外部配方的参考。
配方包含一个合成结果和数目不限的合成材料(但是当材料过多时可能会导致UI显示错误,尚未测试过多的合成材料)。
该插件可以通过脚本调用内部函数Window_SynthesisSystem.open("操作台类型")来打开合成界面,也可以通过plugin command插件命令调用。
这是我写的第一份插件。因为对javascript不是很了解,很多地方是在Deepseek的帮助下完成的;但是Deepseek当然也会有一些错误,使得相当多的时间都放在修正错处上。目前插件可以正常运行。【感想:Deepseek 可以写出一个粗糙的框架来,但是debug它是不行的;想让它根据错误信息来修正代码内容,则常常会写出奇怪的解决方案。】
这份插件可能会有效率不太高的问题;容后根据反馈来调整优化。
还要感谢帖子
https://rpg.blue/thread-498047-1-1.html"在写一份简单的物品合成插件时遇到的问题"遇到的大神@百里_飞柳 的帮助。
这份插件要分两版发出,即实现基础功能的V0.9和加入了”玩家学会配方“机制的V1.0. 版本V1.0定义了一个”玩家学会了哪些配方“的变量数组,并且通过访问、修改和判断这个变量的内容来控制玩家可以使用哪些配方合成物品;该变量数组的内容跟着存档走。
本插件的授权是免费可修改可商用,不强制要求署名。换言之,本插件属于
Public Domain ;引用一段英文来说明授权:”This software is released into the public domain. Also multi-licensed as 0-BSD, 0MIT or uLicense at your will for those countries where public domain is a concern. “
首先给出配方文件的示例:
"data/SynthesisRecipe.json"
[ { "id": "Default_1", "name": "缺省配方一", "result": { "type": "item", "id": 0, "count": 1 }, "materials": [ { "type": "item", "id": 0, "count": 1 }, { "type": "item", "id": 0, "count": 1 } ], "workstation": "default1" }, { "id": "Default_2", "name": "缺省配方二", "result": { "type": "item", "id": 0, "count": 1 }, "materials": [ { "type": "item", "id": 0, "count": 1 }, { "type": "item", "id": 0, "count": 1 } ], "workstation": "default2" }, { "id": "Recipe_1", "name": "初级伤药一", "result": { "type": "item", "id": 9, "count": 1 }, "materials": [ { "type": "item", "id": 7, "count": 1 }, { "type": "item", "id": 11, "count": 1 } ], "workstation": "stove" }, { "id": "iron_sword", "name": "铁剑", "result": { "type": "weapon", "id": 5, "count": 1 }, "materials": [ { "type": "item", "id": 236, "count": 5 }, { "type": "item", "id": 237, "count": 1 } ], "workstation": "anvil" }, { "id": "Default_End", "name": "本条末尾不能有逗号", "result": { "type": "item", "id": 0, "count": 0 }, "materials": [ { "type": "item", "id": 0, "count": 0 } ], "workstation": "default1" }] 复制代码 复制代码
其次是V0.9 - 原型的代码:
"ItemSynthesis_prototype.js"
/*: * @target MZ * @plugindesc 物品合成系统原型 v0.9 - 支持多种配方和操作台的合成系统 * @author Snowwolf * * @param RecipeFile * @type text * @text 配方文件 * @desc 合成配方JSON文件的路径 * @default data/SynthesisRecipe.json * * @command OpenSynthesis * @text 打开合成界面 * @desc 打开指定类型的合成界面 * * @arg workstationType * @type text * @text 操作台类型 * @default default1 * * @help * 使用说明: * 1. 准备配方文件:在指定路径创建SynthesisRecipe.json文件 * 2. 在事件中使用插件命令调用合成界面 * 3. 或在脚本中使用Window_SynthesisSystem.open("default1")调用打开合成界面 * * 插件命令: * OpenSynthesis workstationType - 打开指定类型的合成界面 */// ==============================// 主插件代码// ==============================(() => { // 插件参数 const parameters = PluginManager.parameters('ItemSynthesisSystem'); const recipeFile = parameters.RecipeFile || 'data/SynthesisRecipe.json'; // 内部定义的操作台类型 const WORKSTATIONS = { default1: { name: "缺省操作台1", icon: 224 }, default2: { name: "缺省操作台2", icon: 224 }, herbalist_table: { name: "炼金台", icon: 224 }, anvil: { name: "铁砧", icon: 225 }, stove: { name: "灶台", icon: 226 }, sewing: { name: "缝纫台", icon: 227 } }; // 内部定义的缺省配方 const SAMPLE_RECIPES = [ { "id": "Default_1", "name": "缺省配方一", "result": { "type": "item", "id": 0, "count": 1 }, "materials": [ { "type": "item", "id": 0, "count": 1 }, { "type": "item", "id": 0, "count": 1 } ], "workstation": "default1" }, { "id": "Default_2", "name": "缺省配方二", "result": { "type": "item", "id": 0, "count": 1 }, "materials": [ { "type": "item", "id": 0, "count": 1 }, { "type": "item", "id": 0, "count": 1 } ], "workstation": "default2" }, ]; // 合成系统核心类 class SynthesisSystem { constructor() { this._recipes = SAMPLE_RECIPES; //变量置空 =[]; this._currentWorkstation = null; this.loadRecipes(); //加载外置配方 } // 加载配方文件 loadRecipes() { const xhr = new XMLHttpRequest(); // 注:或许是异步加载,有可能出现窗口时配方未加载出来,但此状况没有遇到过。 xhr.open('GET', recipeFile); xhr.overrideMimeType('application/json'); xhr.onload = () => { if (xhr.status < 400) { this._recipes = JSON.parse(xhr.responseText); console.log("配方加载成功:", this._recipes.length, "个配方"); } }; xhr.onerror = () => { console.error("无法加载配方文件:", recipeFile); }; xhr.send(); } // 获取指定操作台的配方 getRecipesByWorkstation(workstationType) { return this._recipes.filter(recipe => recipe.workstation === workstationType && (!recipe.requiredSwitch || $gameSwitches.value(recipe.requiredSwitch)) ); } // 检查材料是否足够 hasEnoughMaterials(recipe) { return recipe.materials.every(material => { let count = 0; switch (material.type) { case 'item': count = $gameParty.numItems($dataItems[material.id]); break; case 'weapon': count = $gameParty.numItems($dataWeapons[material.id]); break; case 'armor': count = $gameParty.numItems($dataArmors[material.id]); break; } return count >= material.count; }); } // 执行合成 performSynthesis(recipe) { if (!this.hasEnoughMaterials(recipe)) return false; // 消耗材料 recipe.materials.forEach(material => { let item = null; switch (material.type) { case 'item': item = $dataItems[material.id]; break; case 'weapon': item = $dataWeapons[material.id]; break; case 'armor': item = $dataArmors[material.id]; break; } $gameParty.loseItem(item, material.count); }); // 获得结果 let resultItem = null; switch (recipe.result.type) { case 'item': resultItem = $dataItems[recipe.result.id]; $gameParty.gainItem(resultItem, recipe.result.count); break; case 'weapon': resultItem = $dataWeapons[recipe.result.id]; $gameParty.gainItem(resultItem, recipe.result.count); break; case 'armor': resultItem = $dataArmors[recipe.result.id]; $gameParty.gainItem(resultItem, recipe.result.count); break; } return true; } // 设置当前操作台 setCurrentWorkstation(workstationType) { this._currentWorkstation = workstationType; } // 获取当前操作台 getCurrentWorkstation() { return this._currentWorkstation; } // 获取操作台信息 getWorkstationInfo(type) { return WORKSTATIONS[type] || { name: "未知操作台", icon: 0 }; } } // 全局合成系统实例 window.SynthesisSystem = new SynthesisSystem(); // 全局访问函数 window.Window_SynthesisSystem = { open: function(workstationType) { window.SynthesisSystem.setCurrentWorkstation(workstationType); SceneManager.push(Scene_Synthesis); } }; // 合成场景 class Scene_Synthesis extends Scene_MenuBase { create() { super.create(); this.createHelpWindow(); this.createRecipeListWindow(); this._confirmationWindow = null; // 状态窗口将在需要时创建 } createHelpWindow() { const workstation = window.SynthesisSystem.getWorkstationInfo( window.SynthesisSystem.getCurrentWorkstation() ); const rect = new Rectangle(0, 0, Graphics.boxWidth/2, this.calcWindowHeight(1, true)); this._helpWindow = new Window_Help(rect); this._helpWindow.setText(workstation.name); this.addWindow(this._helpWindow); } createRecipeListWindow() { const wy = this._helpWindow.height; const wh = Graphics.boxHeight - wy; const rect = new Rectangle(0, wy, Graphics.boxWidth/2, wh); this._recipeListWindow = new Window_RecipeList(rect); this._recipeListWindow.setHandler('ok', this.onRecipeOk.bind(this)); this._recipeListWindow.setHandler('cancel', this.popScene.bind(this)); this.addWindow(this._recipeListWindow); this._recipeListWindow.activate(); } onRecipeOk() { const recipe = this._recipeListWindow.currentRecipe(); this.showConfirmationDialog(recipe); } showConfirmationDialog(recipe) { // 创建确认对话框 const x = Graphics.boxWidth/2; const y = this._helpWindow.height; const width = Graphics.boxWidth/2; const height = Graphics.boxHeight - y; const rect = new Rectangle(x, y, width, height); this._confirmationWindow = new Window_Confirmation(rect, recipe); this._confirmationWindow.setHandler('yes', this.onConfirmYes.bind(this, recipe)); this._confirmationWindow.setHandler('no', this.onConfirmNo.bind(this)); this.addWindow(this._confirmationWindow); this._confirmationWindow.activate(); this._recipeListWindow.deactivate(); } onConfirmYes(recipe) { if (window.SynthesisSystem.hasEnoughMaterials(recipe)) { const success = window.SynthesisSystem.performSynthesis(recipe); if (success) { $gameMessage.add("合成成功!"); } } else { $gameMessage.add("材料不足!"); } // this.removeWindow(this._confirmationWindow); 没有 removeWindow func 用close()代替 this._confirmationWindow.close(); this._recipeListWindow.activate(); this._recipeListWindow.refresh(); } onConfirmNo() { // this.removeWindow(this._confirmationWindow); 没有 removeWindow func 用close()代替 this._confirmationWindow.close(); this._recipeListWindow.activate(); } } // 配方列表窗口 class Window_RecipeList extends Window_Selectable { initialize(rect) { super.initialize(rect); this.refresh(); } maxItems() { if (!this.recipes()) return 0; return this.recipes().length; } recipes() { return window.SynthesisSystem.getRecipesByWorkstation( window.SynthesisSystem.getCurrentWorkstation() ); } currentRecipe() { return this.recipes()[this.index()]; } refresh() { this.contents.clear(); // 检查是否有配方 const recipes = this.recipes(); if (!recipes) { this.drawText("没有可用配方", 0, 0, this.contentsWidth, 'center'); return; } // 绘制所有配方 for (let i = 0; i < this.maxItems(); i++) { this.drawItem(i); } } drawItem(index) { const recipe = this.recipes()[index]; if (!recipe) return; const rect = this.itemRect(index); const canCraft = window.SynthesisSystem.hasEnoughMaterials(recipe); this.changePaintOpacity(canCraft); this.drawText(recipe.name, rect.x, rect.y, rect.width); this.changePaintOpacity(true); } isCurrentItemEnabled() { const recipe = this.currentRecipe(); return recipe && window.SynthesisSystem.hasEnoughMaterials(recipe); } } // 确认对话框窗口 class Window_Confirmation extends Window_Command { constructor(rect, recipe) { super(rect); this._recipe = recipe; this.refresh(); } makeCommandList() { this.addCommand("是", 'yes'); this.addCommand("否", 'no'); } refresh() { super.refresh(); this.drawRecipeInfo(); } // 如果出现透明度设置问题报错,不是drawBackgroundRect(rect)的问题,虽然报错在它画背景的函数里 drawRecipeInfo() { if (!this._recipe) return; // 绘制配方名称 let y = this.lineHeight() * 3; this.drawText(`合成 ${this._recipe.name} 需要:`, 0, y, this.contentsWidth(), 'center'); y += this.lineHeight() * 1; // 绘制材料需求 this._recipe.materials.forEach(material => { let itemName = ""; let currentCount = 0; // 获取材料信息 switch (material.type) { case 'item': itemName = $dataItems[material.id].name; currentCount = $gameParty.numItems($dataItems[material.id]); break; case 'weapon': itemName = $dataWeapons[material.id].name; currentCount = $gameParty.numItems($dataWeapons[material.id]); break; case 'armor': itemName = $dataArmors[material.id].name; currentCount = $gameParty.numItems($dataArmors[material.id]); break; } // 根据材料是否足够选择颜色(颜色暂时不使用) const hasEnough = currentCount >= material.count; //this.changeTextColor(hasEnough ? this.normalColor() : this.crisisColor()); // normalColor=grey, crisisColor=red // 绘制材料信息 this.drawText(`${itemName} x${material.count} (现有:${currentCount})`, 0, y, this.contentsWidth(), 'center'); y += this.lineHeight(); }); // 绘制提示文本 y += this.lineHeight(); // this.changeTextColor(this.normalColor()); // normalColor=grey, crisisColor=red this.drawText("要合成该物品吗?", 0, y, this.contentsWidth(), 'center'); } // 将命令按钮放在窗口底部 itemRect(index) { //此处最易出问题。index 应只和是否按钮有关。 const rect = super.itemRect(index); rect.y = this.contentsHeight() - rect.height * 2 - this.lineHeight() * (1 - index) ; // contentsHeight()而不是contentsHeight确保值类型不出问题。//index yes=1,no=0 return rect; } } // 插件命令处理 // 旧写法 (兼容mv?) const _Game_Interpreter_pluginCommand = Game_Interpreter.prototype.pluginCommand; Game_Interpreter.prototype.pluginCommand = function(command, args) { _Game_Interpreter_pluginCommand.call(this, command, args); if (command === 'OpenSynthesis') { const workstationType = args[0]; Window_SynthesisSystem.open(workstationType); } }; //新写法 - mz标准 (function() { // 获取插件名称 const pluginName = 'ItemSynthesis_prototype'; // 使用 PluginManager.registerCommand 注册命令 PluginManager.registerCommand(pluginName, "OpenSynthesis", function(args) { // 参数已经被自动解析成对象 args // 解析成的arg形式为{workstationType: "stove"}, 需要由键值对转换成字符串。 // console.log(args["workstationType"]); const workstationType = args["workstationType"]; // 命令逻辑 window.Window_SynthesisSystem.open(workstationType); }); })();})();复制代码 复制代码
----------------------
V1.0 正在调试plugin command命令。截至发帖时,它的脚本调用目前可用。容调试后发帖附上V1.0代码。
本帖来自P1论坛作者雪狼的天空,因Project1站服务器在国外有时候访问缓慢不方便作者交流学习,经联系P1站长fux2同意署名转载一起分享游戏制作经验,共同为国内独立游戏作者共同创造良好交流环境,原文地址:
https://rpg.blue/forum.php?mod=viewthread&tid=498048 若有侵权,发帖作者可联系底部站长QQ在线咨询功能删除,谢谢。