//============================================================================= // RPG Maker Plugin - Formula Interpreter // FormulaInterpreter.js //============================================================================= /*: * @target MZ * @plugindesc 公式解析器 * @author Limpid * * @help * * 【语法说明】 * v[id] / V[id] : 变量 (小写初始化获取,大写实时获取) * s[id] / S[id] : 开关 (1/0) * r[n] / R[n] : 随机数 (0 到 n) * g / G : 金钱 * i[id] / I[id] : 物品数量 * l[id] / L[id] : 角色等级 * p[id.attr] / P[id.attr] : 角色特定属性 (如 P[1.hp]) * ^prop^ : 动态引用宿主对象的属性 (如 ^x^, ^target.hp^) * @^prop^ : 静态引用宿主对象的属性 (初始化时固定) * * 内置函数: a(x) [绝对值], s(max, i) [正弦], c(max, i) [余弦] */ var Imported = Imported || {}; Imported.FormulaInterpreter = true; function FormulaInterpreter() { throw new Error("This is a static class"); } /** * 内部数学方法映射 (内联化处理) */ FormulaInterpreter.methods = { a: "Math.abs", s: "(max, i) => Math.sin((Math.PI / 2) / (Number(max) || 1) * (Number(i) || 0))", c: "(max, i) => Math.cos((Math.PI / 2) / (Number(max) || 1) * (Number(i) || 0))" }; /** * 语法解析注册表 */ FormulaInterpreter.registry = { static: { 'v': (id) => $gameVariables.value(Number(id)), 's': (id) => $gameSwitches.value(Number(id)) ? 1 : 0, 'r': (n) => Math.random() * Number(n), 'g': () => $gameParty.gold(), 'i': (id) => $gameParty.numItems($dataItems[Number(id)]), 'l': (id) => $gameActors.actor(Number(id))?.level || 0, 'p': (id_attr) => { const [id, attr] = id_attr.split('.'); return $gameActors.actor(Number(id))?.[attr] || 0; } }, dynamic: { 'V': (id) => `$gameVariables.value(${id})`, 'S': (id) => `($gameSwitches.value(${id}) ? 1 : 0)`, 'R': (id) => `(Math.random() * ${id})`, 'G': () => `$gameParty.gold()`, 'I': (id) => `$gameParty.numItems($dataItems[${id}])`, 'L': (id) => `$gameActors.actor(${id})?.level || 0`, 'P': (id_attr) => { const [id, attr] = id_attr.split('.'); return `$gameActors.actor(${id})?.${attr} || 0`; } } }; FormulaInterpreter._compilePool = new Map(); /** * 创建响应式参数对象 * @param {Object} config - 公式配置库,如 { x: "^t^ * 2" } * @param {Object} origin - 宿主对象 */ FormulaInterpreter.create = function (config, origin) { if (!origin.params) origin.params = {}; if (!origin._fCache) origin._fCache = {}; if (!origin._fFrame) origin._fFrame = {}; for (let key in config) { const formula = config[key]; const compiled = this.compile(formula, origin); if (compiled.type === 'dynamic') { const func = compiled.func; Object.defineProperty(origin.params, key, { get: function () { const now = Graphics.frameCount; if (origin._fFrame[key] === now) return origin._fCache[key]; const val = func(origin) ?? 0; origin._fCache[key] = val; origin._fFrame[key] = now; return val; }, enumerable: true, configurable: true }); } else { origin.params[key] = compiled.value; } } }; /** * 核心编译逻辑 */ FormulaInterpreter.compile = function (formula, origin) { if (typeof formula !== 'string') return { type: 'static', value: Number(formula) || 0 }; if (this._compilePool.has(formula)) return this._compilePool.get(formula); let f = formula; f = f.replace(/@\^([\w.]+)\^/g, (_, p) => { return p.split('.').reduce((acc, k) => (acc && acc[k] !== undefined ? acc[k] : 0), origin); }); let last; const staticRegex = /([a-z])\[([^\[\]]+)\]/g; do { last = f; f = f.replace(staticRegex, (match, type, content) => { const handler = this.registry.static[type]; return (handler && !match.includes('(')) ? handler(content) : match; }); staticRegex.lastIndex = 0; } while (last !== f); const isDynamic = /\^|[A-Z]\[/.test(f); const jsSyntax = this._toJsSyntax(f); let result; if (!isDynamic) { try { const val = new Function(`return (${jsSyntax})`)(); result = { type: 'static', value: Number(val) || 0 }; } catch (e) { result = { type: 'static', value: 0 }; } } else { result = { type: 'dynamic', func: new Function('origin', `return (${jsSyntax});`) }; } this._compilePool.set(formula, result); return result; }; /** * 语法转换 */ FormulaInterpreter._toJsSyntax = function (f) { let js = f; js = js.replace(/\^([\w.]+)\^/g, (_, p) => { const chain = p.split('.').join('?.'); return `(origin?.${chain} ?? 0)`; }); const dynamicRegex = /([A-Z]+)\[([^\[\]]+)\]/g; while (dynamicRegex.test(js)) { js = js.replace(dynamicRegex, (match, type, content) => { const handler = this.registry.dynamic[type]; return handler ? handler(content) : match; }); dynamicRegex.lastIndex = 0; } js = js.replace(/([a-z]\w*)\(([^)]*)\)/gi, (m, name, args) => { const method = this.methods[name.toLowerCase()]; if (!method) return m; return name.toLowerCase() === 'a' ? `Math.abs(${args})` : `(${method})(${args})`; }); return js; }; //============================================================================= // 示例注入:Scene_Base //============================================================================= const _Scene_Base_initialize = Scene_Base.prototype.initialize; Scene_Base.prototype.initialize = function() { _Scene_Base_initialize.call(this); // 示例数据结构 let data = { x1: "^t^+R[100]", // 随 startX, t 和 变量1 实时变化 y1: "R[100]", // 每次访问 y1 都会获得一个新的随机数 size1: "r[100]", // 仅在初始化时生成一个随机数,之后固定 color1: "'#1e90ff'" // 静态字符串 }; // 假设宿主有一些基础属性 this.startX = 100; this.t = 0; FormulaInterpreter.create(data, this); }; const _Scene_Base_update = Scene_Base.prototype.update; Scene_Base.prototype.update = function() { _Scene_Base_update.call(this); this.t++; if (this.params) { console.log("Current Y1:", this.params.x1); } };
Cache_-7dab9ff6232f98cf.gif (672.6 KB, 下载次数: 11)
| 欢迎光临 Project1 (https://rpg.blue/) | Powered by Discuz! X3.1 |