👆 先点右侧一个「配方格」选中它,再点这里任意方块即可放入(无需手打 ID)。数量用下方输入框控制。
选中配方格 → 点左侧方块放入。点已填入的格子可选中,再点左侧可覆盖。
支持原版方块、模组方块、材料、食物、枪械、装备。选定后自动填好类型与名称,也可再手动修改。
👆 这里列出原版 + 已加载模组 + 本工坊正在编辑的熔炼配方,方便对照。
输入/输出从「已注册」列表选择(方块/材料/食物/模组),无需手打。熔炼完成后产出对应输出物。
👆 这里列出原版 + 已加载模组 + 本工坊正在编辑的燃料,方便对照。
燃料物品从「已注册」列表选择(方块/材料/食物/模组),无需手打。放入熔炉即可作为燃料燃烧。
红石组件:填写名称 / 颜色 / 图标后,必须自己编写代码定义其行为。导出时会先自动注册一个该方块(并把它的 tileId 存入变量),然后原样插入你写的代码,可调用 ModAPI / TILE / TILE_DEF 等。
💡 可创建多个维度。群系与维度分开管理、互通:在下方添加群系时选择它生成到哪个维度,或勾选「生成到原版维度」让它同时出现在主世界/地狱/冰雪。
💡 新手用法:在左侧方块列表里**点一下**就加入群系,右侧给每个方块填个数字当「权重」(越大出现越多,留空=1)。完全不用打字。想偷懒可先选上方「方块主题」预设(演示),再手动删改;文本框与左右列表实时同步。同一维度的多个群系按噪声连续分布、互相接壤(互通)。
💡 在网格上绘制建筑外形,勾选「生成到维度」即可。世界生成时,对应维度里会按概率随机出现这座遗迹(蓝图锚定在区块左上角,尺寸无上限、可跨区块,自动读取你选的方块)。
💡 添加后可在指令下「添加子指令」实现多级(如 /warp a、/warp a b),并为每个层级配置「作用」:消息 / 传送 / 生成怪物 / 切换维度 / 设置血量 / 给予方块。
面向社区开发者:说明模组的识别机制、接口格式与语法。在右侧写代码即可开发高级功能,代码注入到工坊自动生成的模组 IIFE 内,已存在 ModAPI、__tiles、__tileVal。
加载顺序:游戏启动 → ModAPI._loadMods() 先 GET /mods/list 扫描 mods/ 顶层(.mod/.js,排除 manifest.json、server/),再去重 + 回退 manifest.json;也可在「🧩 模组」面板手动导入。
.mod 文件 = JSON 包:{ "name","version","author","description","code" }(兼容 client/source/script)。导入时自动 registerMod(meta) 登记(按 name 去重)再执行 code,无需手写 registerMod。
.js / .client.js 文件 = 纯 JavaScript,运行前已包在 IIFE 中、ModAPI 全局可用。必须自己调用 ModAPI.registerMod({name}) 才会出现在模组列表(工坊导出的 .js 已自动加)。
每个模组最终形态:
// 你的代码填写在 registerMod 之后
(function () {
if (typeof ModAPI === 'undefined') { console.warn("[模组] 未就绪"); return; }
ModAPI.registerMod({ name, version, author, description });
// ← 内容注册 / 事件订阅
})();
运行时可用辅助:__tiles(工坊方块名→tileId 映射)、__tileVal('vanilla:GRASS')(原版方块枚举名→tileId)。
// 方块 → 返回 tileId(number),注入 TILE_DEF
ModAPI.registerBlock({ name, emoji, color, hp, dmg, light,
drop, portal, walkable, slow, transparent, showEmoji });
// showEmoji:false → 世界内只渲染纯色方块,不画 emoji(默认 true)
// 怪物/Boss → 注入 ENEMY_TYPES
ModAPI.registerMob(type, { name, emoji, color, hp, atk, spd,
size, xp, score, detect, atkRange, atkCD, drops,
isBoss, isBigBoss, isFinalBoss });
// 合成配方 → 注入 CRAFT_RECIPES
ModAPI.registerRecipe({ shape:[[3x3 of {id,qty}|null]], result:{...} });
// 物品 / 食物
ModAPI.registerItem({ id, name, emoji });
ModAPI.registerFood({ id, name, emoji, sat });
// 枪械 / 装备
ModAPI.registerGun(gunId, { name, emoji, color, type, tier,
dmg, cd, speed, ammo, pierce? });
ModAPI.registerEquip(eqId, { name, emoji, color, tier, def, maxDur });
// 维度(基础注册)
ModAPI.registerDimension({ id, name, color });
// 自定义指令(玩家输入 /name ... 触发)
ModAPI.registerCommand(name, { desc, run(args, ctx) });
// ctx = { game, cmdFeedback }
// 自定义 UI 面板(自动在菜单栏注入打开按钮)
ModAPI.registerPanel({ id, title, body, autoOpen?, openButton? });
ModAPI.openPanel(id); ModAPI.closePanel(id);
// 区块生成完毕钩子(改写地形/维度)
ModAPI.on('chunk:gen', function(e){ // e={world,cx,cy,chunk}
// e.chunk.tiles[y][x] = tileId
});
// 维度切换钩子
ModAPI.on('dimension:change', function(e){ // e={id,world}
});
// 通用事件总线
ModAPI.on(evt, fn); ModAPI.once(evt, fn); ModAPI.off(evt, fn);
// 维度切换
ModAPI.setDimension(id); ModAPI.getDimensions();
ModAPI.say(text, color?) // 游戏内聊天消息(黄) ModAPI.spawnEnemy(type,x,y) // 生成怪物(坐标缺省围绕玩家) ModAPI.giveItem(item) // 给物品对象(完整结构) ModAPI.giveBlock(tileId,qty)// 给已注册方块物品 ModAPI.teleport(x,y) // 传送玩家(像素坐标) ModAPI.getGame() // 返回游戏对象(player/enemies/world)
例:ModAPI.getGame().player.hp = 100; 直接改血量。
世界生成后这些表被填充(在 chunk:gen/dimension:change 回调里一定已就绪),可直接读写扩展:
ModAPI.TILE // 原版方块枚举 (ModAPI.TILE.GRASS…) ModAPI.TILE_DEF // 方块定义表 ModAPI.ENEMY_TYPES // 怪物类型表 ModAPI.GUN_DEFS // 枪械表 ModAPI.FOOD_DEF // 食物表 ModAPI.EQUIP_TIERS // 装备表 ModAPI.CFG // 配置(CHUNK_SIZE…) ModAPI.World / Player / NPC / MISSILE_DEFS / AMMO_DEFS
下面是一份「尽量覆盖所有可添加功能」的示范,可直接整段复制到右侧编辑器(或保存为 .js 模组)。每个区块都可独立删除。
// ===== 0. 模组登记(.js 必须手写;.mod 会自动登记)=====
ModAPI.registerMod({ name:'示范模组', version:'1.0', author:'你', description:'功能全覆盖示范' });
// ===== 1. 方块(返回 tileId 数字)=====
var crystal = ModAPI.registerBlock({ name:'纯净水晶', emoji:'🔷',
color:'#66aaff', hp:350, light:8, walkable:false, transparent:false, slow:false });
var glow = ModAPI.registerBlock({ name:'辉光水晶矿', emoji:'✨',
color:'#99ccff', hp:220, light:14, walkable:false });
// ===== 2. 怪物 / Boss =====
ModAPI.registerMob('crystal_guard', { name:'水晶守卫', emoji:'👾', color:'#77bfff',
hp:220, atk:18, spd:1.3, size:30, xp:60, score:220, detect:240,
atkRange:36, atkCD:55, drops:'crystal_block', isBoss:false });
ModAPI.registerMob('crystal_lord', { name:'水晶领主', emoji:'💎', color:'#2277ff',
hp:1200, atk:35, spd:0.9, size:46, xp:550, score:1800, detect:320,
atkRange:50, atkCD:75, drops:'glow_crystal', isBoss:true, isBigBoss:true });
// ===== 3. 合成配方(3x3 形状,每格 {id,qty} 或 null)=====
ModAPI.registerRecipe({
shape:[
[{id:'crystal',qty:1}, null, {id:'crystal',qty:1}],
[null, {id:'glow',qty:1}, null],
[{id:'crystal',qty:1}, null, {id:'crystal',qty:1}]
],
result:{ type:'item', id:'crystal_core', qty:1, name:'水晶核心', emoji:'🌟' }
});
// ===== 4. 物品 / 食物 =====
ModAPI.registerItem({ id:'crystal_core', name:'水晶核心', emoji:'🌟' });
ModAPI.registerFood({ id:'crystal_food', name:'水晶果', emoji:'🍇', sat:40 });
// ===== 5. 枪械 / 装备 =====
ModAPI.registerGun('crystal_gun', { name:'水晶枪', emoji:'🔫', color:'#66aaff',
type:'rifle', tier:3, dmg:30, cd:18, speed:14, ammo:30 });
ModAPI.registerEquip('crystal_armor', { name:'水晶甲', emoji:'🛡️', color:'#66aaff',
tier:3, def:25, maxDur:600 });
// ===== 6. 维度 + 群系地形(在 chunk:gen 改写)=====
ModAPI.registerDimension({ id:'crystal_realm', name:'水晶领域', color:'#153880' });
ModAPI.on('chunk:gen', function(e){
if(e.world.dimension !== 'crystal_realm') return; // 只改本维度,别污染主世界
var CS = (ModAPI.CFG && ModAPI.CFG.CHUNK_SIZE) || 16;
for(var y=0;y
照着写基本不会出错,下面是新手最容易踩的坑:
① 所有字符串都要加引号(最最常见!)
// ❌ 错误:中文 / emoji 没加引号 → 直接 SyntaxError,模组无法加载
ModAPI.registerBlock({ name:水晶苔晶, emoji:🌀 });
// ✅ 正确:用单引号或双引号包起来
ModAPI.registerBlock({ name:'水晶苔晶', emoji:'🌀' });
② 接口必须带 ModAPI. 前缀
// ❌ 错误:裸写会被当作未定义变量
registerBlock({ name:'x' });
// ✅ 正确
ModAPI.registerBlock({ name:'x' });
③ 方块/物品用数字 id;原版用 ModAPI.TILE.*
// 自定义方块:用 registerBlock 的「返回值」
var b = ModAPI.registerBlock({ name:'我的块', emoji:'🔷', color:'#3366cc', hp:120 });
e.chunk.tiles[y][x] = b; // b 就是 tileId 数字
// 原版方块:用枚举
e.chunk.tiles[y][x] = ModAPI.TILE.GRASS;
④ 改地形先判断维度,否则污染主世界
ModAPI.on('chunk:gen', function(e){
if(e.world.dimension !== '你的维度id') return; // 务必加这行
...
});
⑤ 同名模组会被去重:第二次用相同 name 调 registerMod 会被忽略,不会重复加载。
⑥ 模组出错不会崩游戏:语法或运行出错时,游戏会弹黄色⚠「模组出错」警告窗(显示模组名与错误),关闭即可继续玩;这不是游戏 bug,按提示改代码重新导入即可。
是什么:光影包(Lighting & Shader,后缀 .las)是独立于模组的画质包,只负责画面表现,不改玩法。模组管理器里是单独的「🌅 光影」标签页,同一时间只能启用一个光影包。
怎么做:主菜单点击「🌅 光影开发」打开独立的光影开发工坊 → 拖滑块调参数 → 点「导出光影包 (.las)」下载 .las。光影工坊与模组工坊完全独立,互不干扰。
.las 文件格式(JSON):
{
"name": "我的光影",
"version": "1.0.0",
"author": "你的名字",
"description": "说明文字",
"format": "las",
"params": {
// ── 普通方块 ──
"blockLightMult": 1.0, // 发光方块辉光倍率
"blockNoise": 0.5, // 表面噪点颗粒
"blockBump": 0.0, // 凹凸/法线(对角渐变)
"blockShadow": 1.0, // 底面/右侧阴影
"blockHighlight": 1.0, // 顶面高光
"blockTone": 1.0, // 群系冷暖色调
// ── 液体 ──
"liquidWave": 1.0, // 水/岩浆波动
"liquidReflect": 0.5, // 水面反光条
"liquidGlow": 0.3, // 岩浆自发光
// ── 环境与后期 ──
"atmosphereDark": 1.0, // 夜晚暗化
"torchRange": 1.0, // 光源半径
"vignette": 1.0, // 屏幕暗角
"saturation": 1.0, // 饱和度(canvas 滤镜)
"contrast": 1.0, // 对比度(canvas 滤镜)
"bloom": 0.0, // 泛光
"shadowRes": 1.0 // 阴影分辨率(预留)
},
"code": "ShaderAPI.onBlockDraw(function(ctx){ ... });"
}
数值约定:1.0 = 原版画面,0 = 关闭该效果,>1 = 增强。缺失的键自动补默认值。
ShaderAPI 接口(写在 code 里,已注入 ShaderAPI 变量,不要写 ModAPI 前缀):
ShaderAPI.onBlockDraw(cb) // 每个非液体方块绘制后调用 ShaderAPI.onLiquidDraw(cb) // 每个水/岩浆方块绘制后调用 ShaderAPI.onAtmosphereDraw(cb) // 昼夜/光照叠加层绘制后(每帧一次) ShaderAPI.onPostDraw(cb) // 全世界绘制完、HUD 之前(每帧一次,后期处理) ShaderAPI.params() // 取当前生效参数对象 ShaderAPI.getGame() // 取 Game 实例 ShaderAPI.log(...) / .warn(...) // 打日志
回调参数 ctx:
// onBlockDraw / onLiquidDraw
{ g, // canvas 2D 上下文(直接用它画)
game, tile, def,
tx, ty, // 方块的世界格子坐标
wx, wy, // 方块的世界像素坐标
px, py, // 屏幕坐标(左上角)
sw, sh, // 屏幕上的宽高(已含缩放)
T, zoom,
isLiquid, isWater, isLava,
params } // 当前生效参数
// onAtmosphereDraw / onPostDraw
{ g, game, canvas, width, height, zoom, params }
完整示例:水面加青色扫描线 + 全屏做旧色调
// 液体:横向流动的青色亮条
ShaderAPI.onLiquidDraw(function(c){
if(!c.isWater) return;
var t = Date.now() * 0.002;
var a = 0.10 + 0.08 * Math.sin(t + c.tx * 0.6);
c.g.fillStyle = 'rgba(120,255,230,' + a.toFixed(3) + ')';
c.g.fillRect(c.px, c.py + c.sh * 0.35, c.sw, Math.max(1, c.sh * 0.08));
});
// 方块:越深的地方越暗(伪 AO)
ShaderAPI.onBlockDraw(function(c){
if(c.ty < 40) return;
var d = Math.min(0.35, (c.ty - 40) / 300);
c.g.fillStyle = 'rgba(0,0,0,' + d.toFixed(3) + ')';
c.g.fillRect(c.px, c.py, c.sw, c.sh);
});
// 后期:全屏暖褐色滤镜
ShaderAPI.onPostDraw(function(c){
c.g.save();
c.g.globalCompositeOperation = 'overlay';
c.g.fillStyle = 'rgba(120,80,30,0.10)';
c.g.fillRect(0, 0, c.width, c.height);
c.g.restore();
});
避坑要点
① 性能模式优先级更高:设置里的「⚡ 性能模式」决定某类效果是否允许运行,光影参数只决定强度。低性能档下会强制关掉噪点/色调等,这是给弱机的保护,不是光影包失效。
② onBlockDraw 每帧对每个可见方块都会调用(数百~上千次),里面不要建对象、不要 createLinearGradient、不要 fillText,否则会掉帧。只用 fillStyle + fillRect 最安全。
③ 改了 g 的状态要还原:动过 globalCompositeOperation、globalAlpha、filter、transform 必须 save()/restore() 配对,否则会污染后续 HUD 绘制。
④ 不要在光影里写玩法:光影包只在渲染阶段被调用,改数值/加物品请写模组(.mod)。
⑤ 加载位置:把 .las 放进 mods/ 目录会被自动扫描;也可在「🧩 模组管理器 → 🌅 光影 → 导入光影」手动选择文件。
⑥ 切换光影会清空方块精灵缓存,切换瞬间可能有一帧卡顿,属正常现象。
⚠ 此代码会在游戏内直接执行;语法错误将导致该模组加载失败。建议先在「👁 预览 & 导出」查看生成的完整代码,确认无误再导出。
当打开旧版 / 外部模组时,无法被结构化还原为表单的 群系 / 世界生成逻辑 与 指令运行逻辑 会原样保留在这里(保证零丢失,可继续编辑后导出)。正常情况下本页为空。
// 点击其他标签页添加内容后回来查看生成的代码...
ShaderAPI.onBlockDraw(cb) / onLiquidDraw(cb) / onAtmosphereDraw(cb) / onPostDraw(cb) 插入自定义绘制;回调参数 ctx 含 g(canvas 2D 上下文)、px/py/sw/sh(屏幕坐标尺寸)、tx/ty(格子坐标)、tile/def、isLiquid/isWater/isLava、params(当前参数)。编写规则详见左侧文档。// 点击「导出光影包」下载 .las 文件...
/time、/day、/night 手动设定时间。mods/ 文件夹 → 开局或点「🔄 扫描」即自动识别,无需登记 manifest.json{name,version,author,description,code}。直接打开 file:// 时请用「📂 导入」手动选择。mods/ 文件夹 → 开局或点「🔄 扫描」即自动识别。{name,version,author,description,format:"las",params:{...},code:"..."}