本篇教程由作者设定使用 CC BY-NC-ND 协议。

本教程来自CrT官方Wiki文档,链接在此,本教程不叙述基本的ZenScripts语法,请自行前往友谊妈的文档阅读!

同时适用于GTCE及他的所有分支与复刻版,例如GTCEu,但我并不能保证他们不会对CrT兼容的部分做出改变!


GTCE&CrT

GTCE的魔改并非与原版工作台一样,而是使用了RecipeMap这个工具,所以写法也和原版修改法不同!

如果想要魔改使用RecipeMap的配方,必须要导入包

import mods.gregtech.recipe.RecipeMap


机器

需要使用

RecipeMap.getByName(机器名称)

来获取指定合成表。

也可以用赋值写法,例如

val blast_furnace = mods.gregtech.recipe.RecipeMap.getByName("blast_furnace");

接下来只需要blast_furnace来代替blast_furnace的RecipeMap.getByName了。


机器名称

那么你要问了,机器的名称是什么呢?

这是CrT文档给出的名称列表

  • 压缩机: compressor

  • 提取机: extractor

  • 打粉机: macerator

  • 洗矿厂: orewasher

  • 热力离心机: thermal_centrifuge

  • 电炉: furnace

  • 微波炉: microwave

  • 组装机: assembler

  • 冲压机床: forming_press

  • 流体灌装机: fluid_canner

  • 等离子电弧炉: plasma_arc_furnace

  • 电弧炉: arc_furnace

  • 筛选机: sifter

  • 精密激光蚀刻机: laser_engraver

  • 搅拌机: mixer

  • 高压釜: autoclave

  • 电磁选矿机: electromagnetic_separator

  • 两极磁化机: polarizer

  • 化学浸洗器: chemical_bath

  • 酿造室: brewer

  • 流体加热器: fluid_heater

  • 蒸馏室: distillery

  • 发酵槽: fermenter

  • 流体固化器: fluid_solidifier

  • 流体提取器: fluid_extractor

  • 离心机: centrifuge

  • 电解槽: electrolyzer

  • 高炉: blast_furnace

  • 聚爆压缩机: implosion_compressor

  • 真空冷冻机: vacuum_freezer

  • 化学反应釜:chemical_reactor

  • 蒸馏塔: distillation tower

  • 石油裂化机: cracker

  • 热解炉: pyro

  • 线材轧机: wiremill

  • 卷板机: metal_bender

  • 合金炉: alloy_smelter

  • 灌装机: canner

  • 车床: lathe

  • 板材切割机: cutting_saw

  • 压模机: extruder

  • 锻造锤: forge_hammer

  • 打包机: packer

  • 解包机: unpacker

  • 柴油发电机: diesel_generator

  • 燃气轮机: gas_turbine

  • 蒸汽轮机: steam_turbine

  • 等离子发电机: plasma_generator

尚未实现:

  • UU物质生成机: uuamplifier

  • 热力发电机: thermal_generator

  • 半流质发电机: semi_fluid_generator

  • 聚变反应堆: fusion_reactor

非普通RecipeMap

土高炉的合成表注册有所不同,这是CrT文档给出的实例,用于修改Primitive Blast Furnace的配方。

import mods.gregtech.recipe.PBFRecipeBuilder;  //导包

//Primitive Blast Furnace 防火砖高炉
PBFRecipeBuilder.start()
    .input(<ore:ingotCompressedWroughtIron> * 1)  //输入物品:一个二重锻铁锭
    .output(<ore:ingotSteel>.firstItem * 1)  //输出物品:钢锭
    .duration(250)  //配方时间,单位tick
    .fuelAmount(2)  //燃料数量?
    .buildAndRegister();  //配方注册

//Remove recipes from PBF 从防火砖高炉移除合成表
import mods.gregtech.recipe.RecipeMaps;

for recipe in RecipeMaps.getPrimitiveBlastFurnaceRecipes() {
    recipe.remove();  //这时可以使用一般的配方删除方式

RecipeMap

现在来说说RecipeMap的用法。首先需要导包,然后调用构建器:

recipeMap.recipeBuilder()

来调出一个对应机器的RecipeMap。

你可以像开头那样使用赋值法

//使用电力高炉举例
val blast_furnace = mods.gregtech.recipe.RecipeMap.getByName("blast_furnace");
blast_furnace.recipeBuilder() //这里使用的recipeMap是第二行的变量
    .inputs(<ore:ingotCompressedWroughtIron> * 1)  //输入物品:一个二重锻铁锭
    .fluidInputs([<liquid:oxygen> * 500])  //流体输入:500mB氧气(ILiquidStack可以使用CrT的指令/ct liquids获取,输入这个指令后会输出所有的ILiquidStack到CrT的log)
    .outputs(<ore:ingotSteel>.firstItem * 1)  //输出物品:一个钢锭
    .property("temperature", 1000)  //这是物品被烧炼的最低的温度。
    .duration(40)  //配方时间:40tick
    .EUt(120)  //输入能量:120gteu/tick
    .buildAndRegister();  //构建并注册配方

这里的.property是一个特殊的setter,他有一些特殊的方法(""内的字符串)用于不同的机器。

名称描述
explosives聚爆压缩机配方的炸药数量。
circuit可以使用编程电路的机器可以使用于配置编程电路来用于配方中。
tempurature高炉配方使用,配置配方启动最低需要的温度

尚未实现的:

名称描述
amplifier用于uu增幅液生产器,输出的uu增幅液数量,单位mB
eu_to_start用于聚变反应堆,配方开始聚变需要的GTEU量,单位GTEU。

你可以像使用 "temperature" 一样使用它们。

例如

.property("circuit", 1)  //1代表的是编程电路的配置,可以是从0~24的值
.property("explosives", 10)  //10是使聚爆压缩机配方工作的炸药数量

一些可用的RecipeMapSetter(就是在recipeBuilder()之下的setter)

.inputs()  //配方需要输入的物品
.fluidInputs()  //配方需要的流体,注,这里需要ILiquidStack才行。
.outputs()  //配方输出的物品
.fluidOutputs()  //配方输出的流体,需要ILiquidStack
.chancedOutput(<IItemStack>, 0-10000, 0-10000) (10000是100%,即最小0.01%是1) // (物品-IItemStack, 初始输出概率, 每一级输出概率提升(注意:概率这里的数字需要用%换算)
.duration()  //配方时间单位tick
.notConsumable(X)  //这里的X可以是CrT ID(原文如此,大概表示的是Ingredient),他的作用和.inputs相同,但是由这个setter配置的输入物品不会在配方里消耗!
.hidden()  //默认不起用,只要使用了这个setter,那么这个Recipe就不会显示在JEI(即Just Enough Items模组或他的任何分支,复刻)里。
.propety("propety_method" int)  //见上方
.chancedOutput(<IItemStack>, 0-10000, 0-10000) (10000是100%,即最小0.01%=1) //与前面的.chanceOutput大致相同,但只有部分机器会使用这个语句,最不可用的一类是打粉机。(原文如此,具体有何不同未知?)
.EUt()  //每tick的EU消耗

移除机器配方

想要移除一个机器的配方是由这个配方的实例自身完成的,所以你需要先找到这个合成表。

例如:

val compressor as RecipeMap = RecipeMap.getByName("compressor");  //还是熟悉的赋值写法,这次写压缩机

//找到这个配方的(电压, 配方输入的物品, 配方输入的流体,没有即填写null即可)
compressor.findRecipe(2, [<minecraft:redstone>], null).remove();<  //配方寻找并删除此配方

材料-Materials

GTCE的材料系统是非常强大的,它利用metaitem注册来简单快速的注册一系列的锭板块粉粒之类的物品,并且你只需要输入特殊的颜色代码,就可以让GTCE来帮你使用颜色渲染模版。

让我们开始吧!


Material-物品材料注册

首先依然是导包:

import mods.gregtech.material
import mods.gregtech.material.Material

这俩包里包含了GregTech所有关于Material系统的类和对象。


可用的所有属性(用于material注册):

名字类型描述
colorint新材料的RGB格式的颜色代码,格式RGB
chemicalFormulastring新材料的化学式,例如水的化学式是H2O
iconSetMaterialIconSet新材料的渲染材质模版集,一会会讲。
componentsImmutableList这个材料的组成,下面会讲(?)
generationFlagsRawlong这个材料的生成标志。下面会讲(?)
elementElement这个材料的组成元素,大概是给合金材料的(?)

再来看看取值器:

名称类型描述
radioactivebool该材料是否带有放射性,有则true,无则false
protonslong原文中未标注,大概是材料中的质子数量。(?)
neutronslong原文中未标注,大概是材料中的中子数量。(?)
masslong原文中未标注,大概是材料的质量。(?)
densitylong原文中未标注,大概是材料的密度。(?)
camelCaseStringstring原文中未标注,大概是与驼峰有关(?)
unlocalizedNamestring未知(?)
localizedNamestring大概是材料的本地化名,仅限客户端。(?)
namestring材料注册名,全小写。

材料方法:


名称(参数)描述
addFlags(String... flagNames)添加生成标志
hasFlag(String 标志名称)是否有生成标志(?)



FluidMaterial流体材料注册

流体材料是Material的超类,所有成员依然可用。

可用参数:

名称属性描述
fluidTemperatureint原文中未标注,大概是流体温度。(?)

取值器:

名称属性描述
hasFluidbool原文中未标注,大概是确定这个流体是否是液体的属性,true则这个流体属于液体,反之则false。
hasPlasmabool原文中未标注,大概是确定这个流体是否是等离子体的属性,true则是,反之则false。
isGaseousbool原文中未标注,大概是确定这个流体是否是气体的属性,true则是,反之则false。
fluidIFluidDefinition材料流体。(?)IFluidDefinition定义了ILiquidStack[包括Vanila/Liquids/ILiquidStack],此接口允许你更改流体属性。
plasmaIFluidDefinition材料等离子体。(?)

DustMaterial-粉材料注册

他的超类是FluidMaterial,所有成员依然可用。

可用参数:

名称属性描述
oreMultiplierint粉碎矿石在研磨机内产出物品的倍数。(例如输入一个粉碎矿石,产出几个就是几倍)
byProductMultiplierint粉碎矿石在研磨机内产出的副产物的倍数。
smeltingMultiplierint物品在烧制时产物倍数。
directSmeltingSolidMaterial烧制这个材料的矿石得到的产物。
washedlnFluidMaterial洗涤该物品所得到的流体产物。
separatedIntoDustMaterial在电磁分离过程中,这种材料的矿石将被分离成这种材料和这种领域所指定的材料。(?)
burnTimeint该材料在熔炼过程中作为燃料可以烧制的时间,如果为0或负值则不成为燃料。

取值器:

名称类型描述
oreByProductsList矿石的产物,格式为List。
harvestLevelint这个材料的块的挖掘等级。



SoildMaterial-固体材料

他的超类是DustMaterial,所有成员依然可用。

可用取值器:

名称属性描述
handleMaterialSoildMaterial将使用这个材料来制作手柄,手柄制作这个材料的工具。(?)
macerateIntoDustMaterial研磨此材料中的任意物品将会得到的产物,默认为自身。(?)

Getter(Getter的具体介绍请看友谊妈的文档):

名称属性描述
toolSpeedfloat用此材料制作的工具的速度,默认1.0f
toolDurabilityint用此材料制作的工具的耐久,0就表示此材料无法制成工具。
toolEnchantmentsList制成这种工具时会自带的附魔,默认是啥也不带。

IngotMaterial-锭材料

他的超类是SoildMaterial,所有成员依然可用。

这个部分比较特殊了,他作为锭材料,可以制成导线,流体管道等特殊设备。

这些属性可以通过下面的Setter设置:

setCableProperties(long 电压等级, int 基础设置安培, int 每格线损)  //电线
setFluidPipeProperties(int 吞吐量, int 最大耐受温度, boolean 是否能传输气体)  //流体管道

CrT文档给出的一个例子:

var ingotMaterial = MaterialRegistry.createIngotMaterial(2052, "test", 0x1a2f3e, "ingot", 1);  //还是赋值
ingotMaterial.setCableProperties(128, 4, 1); // 128EU/tick 4A 1线损/个方块

附魔数据

可以在gregtech.mods.EnchantmentData中找到SolidMaterial#toolEnchantments,它们是带有等级的附魔的内部存储。

它们可以使用crafttweaker.enchantments.IEnchantmentDefinition通过调用enchantmentgetter来转换,而level可以通过levelgetter获得。

要为SoildMaterial中的工具添加附魔,只需调用addToolEnchantment(IEnchantment enchantment), 添加 CraftTweaker 附魔数据类型即可。

var material = MaterialRegistry.get("iron");  //赋值并修改Iron材料
material.addToolEnchantment(<enchantment:minecraft:fortune> * 1);  //为材料的工具添加一个附魔,会在他合成的时候就附上去。

FLAG-材料生成标志

这些标志适用于材料中:

标志名称(不区分大小写)描述
DECOMPOSITION_BY_ELECTROLYZING启用生成电解器分解配方
DECOMPOSITION_BY_CENTRIFUGING启用生成离心机分解配方
BURNING加入使材料会持续燃烧
FALMMABLE加入使材料会成为易燃可燃物。
EXPLOSIVE加入使材料会成为某种爆炸物。
NO_UNIFICATION加入使材料无法完全统一。(?)
NO_RECYCLING加入使任何物品无法回收进行清洗。(?)
DISABLE_DECOMPOSITION加入后禁止生成这种材料和所有以它为成分的材料的分解配方
DECOMPOSITION_REQUIRES_HYDROGEN加入后使分解配方需要额外输入氢气,数量等于输入数量
GENERATE_PLATE为这种材料生成一个板材料,如果是DustMaterial,将生成使用粉压缩成板的压缩机配方,如果是金属材料,将生成使用卷板机的配方,如果发现块材料,则会生成使用板材切割机的配方。
GENERATE_DENSE为这种材料生成一个压缩板材料。
NO_WORKING会使此材料除研磨或熔炼外,无法通过任何其他方式加工,这用于涂层材料。
NO_SMASHING会使此材料无法通过常规金属加工方法技术,因为无法弯曲此类金属,用于较为坚固的材料。
NO_SMELTING会使此材料无法被烧制。
INDUCTION_SMELTING_LOW_OUTPUT会使此材料在感应炉内产出较少。
SMELT_INTO_FLUID会使此材料可以融化成液体(会为这种材料注册液体)。
EXCLUDE_BLOCK_CRAFTING_RECIPES原文无描述,未知作用。
EXCLUDE_PLATE_COMPRESSOR_RECIPE原文无描述,未知作用。
CRYSTALLISABLE加入使此材料被判定为水晶类。(例如绿宝石和钻石)
GENERATE_FLUID_BLOCK加入使系统为这种流体材料产生流体块。
GENERATE_LENSE原文无描述,未知作用。
HIGH_SIFTER_OUTPUT原文无描述,未知作用。
GENERATE_PLASMA加入则生成此材料的等离子体。
STATE_GAS加入会材料状态标记为气体。
GENERATE_ROD原文未描述,大概是为此材料注册杆材料。
GENERATE_GEAR原文未描述,大概是为此材料注册齿轮材料。
GENERATE_LONG_ROD原文未描述,大概是为此材料注册长杆材料。
MORTAR_GRINDABLE加入使这种材料可以用研钵研磨。

材料图标集

需要导包:

import mods.gregtech.material.MaterialIconSet

图标集列表:

  • NONE

  • METALLIC

  • DULL

  • MAGNETIC

  • QUARTZ

  • DIAMOND

  • EMERALD

  • SHINY

  • SHARDS

  • ROUGH

  • FINE

  • SAND

  • FLINT

  • RUBY

  • LAPIS

  • POWDER

  • FLUID

  • GAS

  • LIGNITE

  • OPAL

  • GLASS

  • WOOD

  • LEAF

  • GEM_HORIZONTAL

  • GEM_VERTICAL

  • PAPER

  • NETHERSTAR

具体的图标集内的图标模版可以在格雷模组文件内/assets/gregtech/textures/items/material_sets看具体每个文件夹内都有哪些图标模版。


图标集的Getter是name。

方法有toString(),和静态方法getByName(String name)。


MaterialRegister-材料注册

材料注册表是统一系统中获取、列出和创建材料的帮手。

需要导包

import mods.gregtech.material.MaterialRegistry

实例:

#loader gregtech  //预加载器
import mods.gregtech.material.MaterialRegistry;

//注意,如果没有找到任何东西,返回类型可以为空。
var material = MaterialRegistry.get(materialName);

//列出所有已注册的材料
var materialList = MaterialRegistry.getAllMaterials();

//如果此材料不能作为工具使用,将toolDurability设置为0
// 注意一个@Optional的参数可以省略,它被默认的0所取代。
//意味着宝石和钢锭材料默认不能作为工具使用。
MaterialRegistry.createFluidMaterial(int meta注册ID(不能重复!), String 名称, int 颜色RGB, String 图标集, @Optional MaterialStack[] materialComponents);

MaterialRegistry.createDustMaterial(int meta注册ID, String 名称, int 颜色, String 图标集, int 需要的采掘等级, @Optional MaterialStack[] materialComponents);

MaterialRegistry.createGemMaterial(int meta注册ID, String 名称, int 颜色, String 图标集, int 需要的采掘等级, @Optional MaterialStack[] materialComponents, @Optional float 工具速度, @Optional int 工具耐久);

MaterialRegistry.createIngotMaterial(int meta注册ID, String 名称, int 颜色, String 图标集, int 需要的采掘等级, @Optional MaterialStack[] materialComponents, @Optional float 工具速度, @Optional int 工具耐久, @Optional int 高炉烧制此类材料需要的的温度);

最后的总例子:

#loader gregtech  //预加载器
import mods.gregtech.material.MaterialRegistry;

//创建一个宝石材料,其Tooltip显示化学式。
//这将自动生成一个电解器配方,将这种材料电解它的组成部分。
val dustMaterial = MaterialRegistry.createDustMaterial(700, "test", 0xFFAA33, "dull", 2);
dustMaterial.addFlags(["GENERATE_ORE", "GENERATE_PLATE"]);
.
val gemFancy = MaterialRegistry.createGemMaterial(701, "some_fancy_gemstone", 0x0F3E4E2, "gem_horizontal", 1, [<material:beryllium>*4, <material:silicon>*2, <material:oxygen>*9, <material:hydrogen>*2], 1.0, 0);
//可以使用任何先前注册的材料--包括自定义的材料与GTCE自带材料。
val ingotComplex = MaterialRegistry.createIngotMaterial(702, "complex_alloy", 0xF6872E, "shiny", 1, [<material:copper>*3, <material:electrum>*1, <material:redstone>*9, <material:some_fancy_gemstone>*2], 3.5, 0);

至此,材料部分结束。


DevTech扩展

现在我们来试试DevTech,首先你得加个DevTech模组,按照你现在的GTCE的版本来下载,例如GTCE下载1.0.0版本以下,GTCEu下载1.0.0及以上版本。


Devtech-RecipeMap

又遇到我们熟悉的老朋友----RecipeMap了,这次它是Devtech添加的扩展,它给RecipeMap添加了更多厉害的功能。

首先还是导包:

import mods.devtech.IRecipeMap;

然后就可以开始使用了,首先来看看他的自定义RecipeMapBuilder功能

官方给出的例子:

IRecipeMap.recipeMapBuilder(string 名称)
    .minInputs(int 最小输入)
    .maxInputs(int 最大输入)
    .minOutputs(int 最小输出)
    .maxOutputs(int 最大输出)
    .minFluidInputs(int 最小流体输入,单位mB)
    .maxFluidInputs(int 最大流体输入)
    .minFluidOutputs(int 最小流体输出)
    .maxFluidOutputs(int 最大流体输出)
    .setOverlaySlots(overlaySlots as OverlaySlots...)  //这里的意思是渲染槽位
    .setProgressBar(texture as TextureArea, moveType as MoveType)  //贴图渲染和moveType
    .build();

除了普通的RecipeMapBuilder之外,你还可以使用

IntCircuitRecipeBuilder 和 .buildCircuit(); 即编程电路配方

BlastRecipeBuilder 和 .buildBlast(); 即高炉配方

至于至于具体的使用则需要在RecipeMap内使用,这是RecipeMapBuilder,他只适用于为机器提供槽位和RecipeMap模版。


IRM其他部分

接下来来看看渲染槽OverlaySlot和TextureArea及moveType,这几个部分官方wiki并未提到,起初我以为是官方未完善,其实是没有更新Wiki。通过查找源代码我发现这一部分已经写好了。

首先来看看源代码

需要导入包:

import mods.devtech.TextureArea;
import mods.devtech.MoveType;
import mods.devtech.OverlaySlot;
import mods.devtech.GUITextures;

首先来看看MoveType

源代码给出了三个MoveType,分别是:

VERTICAL 则垂直的
HORIZONTAL 则是横向的
VERTICAL_INVERTED 则垂直倒置

猜测分别是GUI的渲染方向。

而GUI则需要一个TextureArea,它有三个方法,一种是现有的GUI直接搬过来。

使用以下方法:

IOverlaySlots.newOverlaySlot(boolean isOutput, boolean isFluid, ConstantGUITexture texture)

前面的俩个选项可以根据自己的需要来调整,而ConstantGUITexture则需要另外一个包。

我们首先写一个赋值,表示从这个包里get出的GUITexture:

val GUI = IDTTextureArea.get(String name)

此时会返回这个string在GUI中的对应,这里是GUI列表,在文件内寻找前面带@ZenProperty的,后面的空格之后的一串就是他的string name,到括号为止。每个GUI具体对应哪一个我就不瞎猜了,我也没空一个一个试。


Devtech-Overlay

本章将会讲述关于RecipeMapBuilder中Overlay的部分。

需要注意的是,这个Overlay是在机器外部渲染材质,而不是机器内部的OverlaySlot。

需要导入包,根据你的需要来导入:

import mods.devtech.Overlay;  //非自定义材质
import mods.devtech.OverlayRenderer;  //自定义材质导包,未完善
import mods.devtech.OverlayFace;  //自定义材质渲染面

一个可用的Getter:

Overlays.get(string as name);

CostantOverlay

官方给出的overlayName列表,此列表中是GTCE自带的机器材质:

  • coal_boiler

  • lava_boiler

  • solar_boiler

  • primitive_blast_furnace

  • coke_oven

  • alloy_smelter

  • furnace

  • electric_furnace

  • extractor

  • compressor

  • hammer

  • macerator

  • amplifab

  • arc_furnace

  • assembler

  • autoclave

  • bender

  • brewery

  • canner

  • centrifuge

  • chemical_bath

  • chemical_reactor

  • cutter

  • distillery

  • electrolyzer

  • electromagnetic_separator

  • extruder

  • fermenter

  • fluid_canner

  • fluid_extractor

  • fluid_heater

  • fluid_solidifier

  • forge_hammer

  • forming_press

  • lathe

  • microwave

  • mixer

  • ore_washer

  • packer

  • unpacker

  • plasma_arc_furnace

  • polarizer

  • laser_engraver

  • sifter

  • thermal_centrifuge

  • wiremill

  • diesel_generator

  • gas_turbine

  • steam_turbine

一个例子:

val overlay = Overlays.get("steam_turbine");

还是赋值写法。



CustomOverlay

注意这部分自定义未完善。

OverlayRenderer.newOverlay(string 路径, firstFace as OverlayFace, secondFace as OverlayFace)

路径需要一个可以加载Textures的模组或者材质包,例如Resources Loader。

OverlayFace则是一个新类型,分别有:

  • FRONT = 前面

  • BACK = 后面

  • TOP = 顶面

  • BOTTOM = 底部

  • SIDE = 侧面

官方给出的一个例子:

#loader gregtech
import mods.devtech.OverlayRenderer;
import mods.devtech.OverlayFace;
import mods.devtech.machines.RegisterMachine;
import mods.gregtech.recipe.RecipeMaps;



var textures = OverlayRenderer.newOverlay("machines/thingy", OverlayFace.FRONT, OverlayFace.SIDE, OverlayFace.TOP);  
//Then you just slap that variable into the 
machineRegisterMachine.CreateSimpleMachine(1987, "thingy.lv", RecipeMaps.COMPRESSOR_RECIPES, textures, 1);

可以看出来这个机器使用了Compressor的RecipeMap和新建的一个newOverlay,材质的路径在machines/thingy,然后是渲染面,指的是材质的渲染面。

然后按照这个实例来看,你的材质需要放在(以Resources Loader作为实例)resources/gregtech/textures/blocks/machines下方新建对应机器名称文件夹,如果是材质包就是assets/gregtech/textures/blocks/machines下方机器名称子文件夹。



自定义单方块机器

导入包:

import mods.devtech.machines.RegisterMachine;

SimpleMachine简单的机器

RegisterMachine.CreateSimpleMachine(int 机器id,必须是独特的,不能重复, string 本地化名,可以是Lang key, RecipeMap 可以是自定义或者是原版的RecipeMap, OverlayRenderer 这里的Renderer官方没有写wiki,但是有源代码,不完整, int 等级);  //而且Overlay Renderer我看不懂(
RegisterMachine.CreateSimpleMachine(int 机器id, string 本地化名, RecipeMap, Overlays 上面的那个, int 等级);

SimpleGenerator

RegisterMachine.CreateSimpleGenerator(id as int, location as string, fuelRecipeMap as FuelRecipeMap, overlay as OverlayRenderer, tier as int);
RegisterMachine.CreateSimpleGenerator(id as int, location as string, fuelRecipeMap as FuelRecipeMap, overlay as Overlays, tier as int);

请注意:FuelRecipeMap没有完善!这个SimpleGenerator近乎没用。

Macerator esque machines

专门给研磨机的机器创建方式:

RegisterMachine.CreateMacerator(id as int, location as string, recipeMap as RecipeMap, overlay as Overlays, tier as int, int 输出数量,大概是输出格子的意思);  //前面的我就不说了

Chest箱子

RegisterMachine.CreateChest(int 机器id, string 本地化名, SolidMaterial 箱子所用材料, int 一行几格, int 一共几行);

SoildMaterial请看上边的Material部分,但是不一定要自定义的SoildMaterial,可以用原版的SoildMaterial。

Tank-流体储罐

RegisterMachine.CreateTank(int 机器id, string 本地化名, SolidMaterial 水槽使用的材料, int 水槽大小, int 最大纵向, int 最大横向);

最大纵向和最大横向应该指的是水槽叠加最大大小,水槽大小则是液体容量,单位mB。

QuantumChest-量子箱

RegisterMachine.CreateQuantumChest(int 机器id, string 本地化名, int 等级, long 存储);

具体这个存储量为什么是long我也不懂,也没时间试。

QuantumTank-量子缸

RegisterMachine.CreateQuantumTank(int 机器id, string 本地化名, int 等级, int 存储);

这次的存储怎么是int呢(?

FluidCollector

流体收集器

RegisterMachine.CreateFluidCollector(int 机器id, string 机器名称, string 等级, ILiquidStack 流体, int 循环length, int 储罐大小);

好像GTCE原版并没有这种机器,具体作用我不好说。


Devtech-GTCEu

我在写这篇教程准备发表的时候发现Github上的Devtech有一个fork的仓库,是brachy84的GTCEu支持重制版,至于Devtech原版已经停更了。

点击这里打开DevtechCEu的Github页面

需要使用这个版本请打开这个链接下载。


DevtechCEu-新建机器

这个版本的Devtech制作了一个新的新建机器的办法,可以创建机器,锅炉,蒸汽机器,发电机等。需要导入包:

import mods.gregtech.machine.MachineBuilder;
import mods.gregtech.recipe.RecipeMapBuilder;
import mods.gregtech.recipe.RecipeMaps;
import mods.gregtech.recipe.RecipeMap;

所有关于新建机器都基于MachineBuilder对象。

CrT的机器ID从32000开始。

基本名称应包含任何:或层后缀,例如.lv. 这些将自动添加。

等级会被加入ID里,例如青铜级Steam就是+1,钢级Steam则+2,ULV则+3...以此类推。

这意味着您应该为每台机器保留至少 15 个(如果您将使用 MAX,则为 16 个)ID。

(这部分写的我有点不太懂,但反正意思应该是,你的机器如果注册的是从青铜蒸汽一直到最大的MAX电压就需要留下从32000~32016这16个ID给他们使用。)

MachineBuilder的格式

MachineBuilder.create(int id, String baseName)

一些必要的Setter

设置机器的RecipeMap:

setRecipeMap(RecipeMap recipeMap)
.setRecipeMap(RecipeMaps.ASSEMBLER_RECIPES)

这里的RecipeMap可以是原版的其他机器的RecipeMap,也可以是自定义的RecipeMap,下面会提到。


设置机器的材质:

这里的设置指的是外观材质而不是内部GUI。

有三种方法来设置外部材质,但是他只会渲染一个外层,底层的材质会由等级决定,也就是等级为ULV时底层材质则会是ULV机械方块。

方法1:

使用GTCEu原版给出的机器材质,使用模组内部路径来确定材质。路径和材质名称都在这里

例子:

.setRenderer(String 路径)  //格式
.setRenderer("machines/assembler")

以组装机作为示例,所有的路径都应该从machines开始,你可以直接解压CEu的文件内assets/gregtech/textures/blocks/machines内查看所有CEu自带的机器材质。

方法2:

新建一个渲染Overlay,首先导入包:

import mods.gregtech.machine.Renderer;

基本格式则是

.setRenderer(Renderer.create("machines/awesome_machine", "front", "top", "bottom", "back", "side"))

示例给出的材质路径是machines下子文件夹,所以你可以使用Resources Loader或者材质包

ResourcesLoader路径:

resources/gregtech/textures/blocks/machines下子文件夹。

材质包路径:

assets/gregtech/textures/blocks/machines下子文件夹。

你可以定义材质的面,使用缩写f, t, bo, ba, s也仍然有效。

方法3:使用Multiblock Tweaker

由于方法比较难写,所以我不推荐,想写自己去看官方Wiki和Multiblock Tweaker的Wiki。


非必选Setter

首先是机器的电压等级:

给机器添加等级,除非机器不使用电压等级。

最小值为0则ULV,最大值14则MAX。

添加等级有俩种写法:

addTier(int... tier)  //这个办法需要你把所有的等级列出来
.addTier(1, 2, 3, 4)  //这些代表的是你注册的等级

.addTierRange(int minTier, int maxTier) //这个办法需要你最小的等级和最大的等级
.addTierRange(1, 4)  //最后会注册从最小到最大的等级机器

设置为发电机:

也许你需要这个MachineBuilder声明成机器或发电机。

.setGenerator()  //设置为发电机

.setMachine()  //设置为机器

设置机器内流体存储大小:

.setTankScalingFunction(function(tier as int) as int {    
    return 4000;
})  //这是一个Function,他表示所有等级的机器的流体存储容量都为4kmB

蒸汽机器Setter

以下方法只适用于蒸汽机器/蒸汽锅炉。

设置为蒸汽机器/外观设置:

.addSteamTier(boolean highPressure)
.addSteamTier(false)

.addSteamTier(boolean highPressure, boolean bricked)
.addSteamTier(true, true)

这俩个Setter都会使机器变成蒸汽机器,但是他们设置后外观有不同,如果highPressure为true,那么蒸汽机器将会启用钢制蒸汽机器,并且将钢制机器的底层外观渲染变成钢制外壳。如果bricked也为真,那这个机器的底层外观渲染会变成砖砌外壳。

如果俩个都为false,则只会启用普通青铜外壳的机器。


蒸汽机器进度条:

.setProgressBar(boolean highPressure, String path)
.setProgressBar(false, "textures/gui/progress_bar/progress_bar_arrow_bronze.png")

.setProgressBar(boolean highPressure, String path, MoveType moveType)
.setProgressBar(false, "textures/gui/progress_bar/progress_bar_arrow_bronze.png", MoveType.RIGHT)

boolean highPressure设置了他的渲染等级,false则钢制机器不渲染,true则渲染。

path则只需要这一个路径,因为这是青铜蒸汽机器进度条的唯一路径。而MoveType则制定了他的朝向,MoveType有效值只有RIGHT,UP,DOWN,如果安装了Multiblock Tweaker则有其他的方法。


设置蒸汽转化率:

确定此特殊的机器(仅机器,而非锅炉)的蒸汽等于1 EU。
默认为1.0,如果不加入这个Setter默认就没有。

.setSteamConversionRate(boolean highPressure, double conversionRate)
.setSteamConversionRate(true, 1.5)

第一个bool值如果true则启用钢制的后面一个double值则是蒸汽转化。


设置锅炉:

定义蒸汽行为的值。
steamOutput= 每 0.5 秒输出多少蒸汽(高压默认为 24​​0,正常为 100)
coolDownInterval= 锅炉开始冷却需要多长时间(高压默认为 40,正常为 45)
coolDownRate= 机器冷却的速度(默认 1)

setBoilerValues(boolean highPressure, int steamOutput);
setBoilerValues(boolean highPressure, int steamOutput, int coolDownInterval);
setBoilerValues(boolean highPressure, int steamOutput, int coolDownInterval, int coolDownRate);

设置蒸汽存储量:

.setSteamTankSize(boolean highPressure, int tankSize)
.setSteamTankSize(false, 8000)

设置蒸汽机器槽位:

.setSlotOverlay(boolean highPressure, String path, boolean isOutput);
.setSlotOverlay(boolean highPressure, String path, boolean isOutput, boolean isFluid);
.setSlotOverlay(boolean highPressure, String path, boolean isOutput, boolean isFluid, boolean isLast);

如果不是特别特殊的GUI不要使用这个玩意了,有点小麻烦。


注册

.buildAndRegister()

一些例子:

MachineBuilder.create(32000, "assembler")
    .setRecipeMap(RecipeMaps.ASSEMBLER_RECIPES)
    .setRenderer("machines/assembler")
    .addTier(0)
    .addSteamTier(false, true)
    .addSteamTier(true)
    .buildAndRegister();
// creating a recipe map here
val boilerRecipes as RecipeMap = RecipeMapBuilder.create("awesome_boiler")  //自定义RecipeMap的名称
    .setInputs(1, 1)  //输入,俩个格位
    .setOutputs(1)  //输出,输出一个
    .setFluidInputs(1)  //流体输入,一个流体槽
    .setFluidOutputs(1)  //流体输出,一个流体槽
    .setHidden(false) //是否在JEI内隐藏此配方,这里其实可以不写
    .setDurationBar()  //温度条
    .build();  //这里就是自定义RecipeMap
MachineBuilder.create(32015, "awesome_boiler")
    .setRecipeMap(boilerRecipes)
    .setRenderer("generators/boiler/coal")
    .setGenerator()
    .addSteamTier(false, true)
    .addSteamTier(true)
    .buildAndRegister();  //蒸汽锅炉

矿石Flags

需要#loader gregtech在文件的顶部。
需要导包

import mods.gregtech.ore.OrePrefix;

官方给出的一个例子:

OrePrefix.registerOrePrefix(String 名称, float 数量, @Optional String 图标集, @Optional int Flags);

//examples
val curvedPlate as OrePrefix = OrePrefix.registerOrePrefix("plateCurved", 1);
val curvedPlate as OrePrefix = OrePrefix.registerOrePrefix("plateCurved", 1, "plateCurved");
val curvedPlate as OrePrefix = OrePrefix.registerOrePrefix("plateCurved", 1, "plateCurved", 0);

name 定义前缀名称和矿石字典名称。
amount 定义材料数量。锭用1。块用9;
iconType(可选)定义材质图标类型(模型位置)。默认情况下,它使用前缀的名称。位于textures/items/materials_set/内
flags(可选)是标志。

  • 0 = 无(默认)

  • 1 = 启用统一

  • 2 = 自引用

  • 3 = 两者



矿石项目

首先导入包:

import mods.gregtech.material.IMaterialPredicate;
import mods.gregtech.material.Material;

需要一个

OrePrefix.getByName(String name);

现在你需要定义一个生成谓词。它定义了哪种材料应该实际拥有该项目。例如,板项目要求材料要么有锭属性,要么有宝石属性。

官方给出的实例:

curvedPlate.setGenerationPredicate(IMaterialPredicate predicate);
// example
curvedPlate.setGenerationPredicate(function(mat as Material) as bool {
    return mat.hasIngot();
} as IMaterialPredicate);

predicate一个返回布尔值的函数,确定是否应为该材料生成项目。
这将检查材料是否具有锭属性。您可以使用预定义的谓词。

列表:

curvedPlate.setGenerationPredicate(IMaterialPredicate.hasIngot);
curvedPlate.setGenerationPredicate(IMaterialPredicate.hasGem);
curvedPlate.setGenerationPredicate(IMaterialPredicate.hasDust);
curvedPlate.setGenerationPredicate(IMaterialPredicate.hasFluid);

最后将此添加到您的矿石Flag中,以实际创建该项目。

curvedPlate.createMaterialItem();

自动生成配方:

你必须使用一个没有#loader gregtech这个的脚本!
您可以添加方法来自动生成配方。
导入包:

import mods.gregtech.recipe.Utils;
import mods.gregtech.ore.IOreRecipeHandler;
import mods.gregtech.ore.OrePrefix;

然后Get你想要的矿石前缀:

val orePrefix = OrePrefix.getPrefix("orePrefix");

最后使用

orePrefix.generateRecipes(IOreRecipeHandler recipeHandler);
// example
orePrefix.generateRecipes(function(orePrefix as OrePrefix, material as Material) {
    // here you can add any recipes like you normally do
    // example (will add a recipe that requires a hammer and the materials plate in crafting table)
    recipes.addShaped(Utils.item(orePrefix, material), [
        [<ore:craftingToolHardHammer>, Utils.ore("plate", material)]
    ]);
} as IOreRecipeHandler);

Utils.item(orePrefix, material)将返回材料和矿石前缀的项目。欲了解更多信息,请看这里



资源

在 lang 文件中为 ore 前缀添加一个 lang 条目

item.material.oreprefix.plateCurved=Curved %s Plate(%s 将替换为材质)

要为物品提供材质,需要为所需的材质集创建模型,assets/gregtech/models/item/material_sets/

文件名应为小写下划线的材质图标类型。在这种情况下plate_curved.json ,现在您需要在该模型指向的任何位置放置材质。


例子:

#loader gregtech
import mods.gregtech.ore.OrePrefix;
import mods.gregtech.material.IMaterialPredicate;
import mods.gregtech.material.Material;
val curvedPlate as OrePrefix = OrePrefix.registerOrePrefix("plateCurved", 1);
curvedPlate.setGenerationPredicate(function(mat as Material) as bool {
    return mat.hasIngot();
} as IMaterialPredicate);
curvedPlate.createMaterialItem();

例子2:

import mods.gregtech.ore.OrePrefix;
import mods.gregtech.ore.IOreRecipeHandler;
import mods.gregtech.recipe.Utils;
val curvedPlate as OrePrefix = OrePrefix.getPrefix("plateCurved");
curvedPlate.generateRecipes(function(orePrefix as OrePrefix, material as Material) {
    recipes.addShaped(Utils.item(orePrefix, material), [
        [<ore:craftingToolHardHammer>, Utils.ore("plate", material)]
    ]);
    <recipemap:bender>.recipeBuilder()
        .inputs(Utils.ore("plate", material))
        .circuit(3)
        .outputs(Utils.item(orePrefix, material))
        .EUt(24)
        .duration(50)
        .buildAndRegister();
} as IOreRecipeHandler);

说实话这一部分我根本不知道有啥用


材质Flag

您可以创建自定义材料标志来确定是否应该为该项目生成某些东西。
这个例子将只为铜生成弯曲板,因为标志只添加到铜上。

#loader gregtech
import mods.gregtech.MaterialFlag;
import mods.gregtech.MaterialFlagBuilder;
val customFlag = MaterialFlagBuilder.create("my_custom_flag")
   .requireIngot()
   .build();
<material:copper>.addFlags(customFlag);
// using curved plate prefix from above as example
curvedPlate.setGenerationPredicate(function(mat as Material) as bool {
    return mat.hasFlag(customFlag);
} as IMaterialPredicate);

这里创建了一个新Flag,称为my_custom_flag它需要 ingot 属性。这意味着如果材料没有锭属性,它将自动添加。您可以要求各种属性和所有标志。

.requireFlag(String flag) //flag
.requireFlag(MaterialFlag flag)
.requireDust()  //粉
.requireIngot()  //锭
.requireGem()  //宝石
.requireFluid()  //流体
.requirePlasma()  //等离子体
.requireTool()  //工具

StoneType

StoneType会创建其他类型的矿石。要使用先导入包:

import mods.gregtech.StoneType;
import mods.gregtech.material.MaterialBuilder;

现在创建一个代表石头的材料。示例将使用 Erebus umberstone。(可以使用现有材料)

val erebusStone = MaterialBuilder(32000, "umberstone")
    .dust(1)
    .build();

注意CrT ID不能重复。

注意:材料必须具有灰尘、锭或宝石属性!
现在我们创建一个定义块状态的辅助字符串。

val blockState = "erebus:umberstone:type=umberstone";

erebus:umberstone是方块ID。之后,您可以添加定义块状态的属性(使用多个 with )。
最后创建石头类型。

StoneType.create(16, "umberstone", "oreUmberstone", erebusStone, blockState);

参数:

  • 16- 是数字标识,GTCEu 从 0 - 11 添加,建议从 16 开始。(从更高的值开始会导致问题)

  • "umberstone"- 石头类型的名称。几乎可以是任何东西

  • "oreUmberstone"- 是将自动创建的矿石前缀的名称。

  • erebusStone- 是之前创建的Stone

  • blockState- 是我们之前创建的方块状态字符串

  • 可选 -IBlockStateMatcher定义此类型可以在生成中替换的块。(默认只替换石头类型方块状态)

  • 可选Boolean - 定义是否应生成矿石项目。(默认为假)


配方助手

配方助手只能在脚本中使用,而无需预加载器#loader gregtech

导入包:

import mods.gregtech.recipe.Utils;

这个类添加了一些帮助器来获取矿石前缀和材料的项目。
对自定义矿石前缀配方处理程序很有用。

使用方式:

Utils.item("minecraft:clay", 1)                             //返回<minecraft:clay:1> (1是他的meta值)
Utils.item(OrePrefix.getPrefix("ingot"), <material:copper>) //返回这个material集中的ingot类型物品,返回<metaitem:ingotCopper>
Utils.item("ingot", <material:copper>)                      //上一个的缩减版,返回<metaitem:ingotCopper>
Utils.item(OrePrefix.getPrefix("ingot"), "copper")          //再次缩减,返回<metaitem:ingotCopper>
Utils.item("ingot", "copper")                               //返回<metaitem:ingotCopper>
Utils.ore("ingotCopper")                                    //返回<ore:ingotCopper>
Utils.ore("ingot", "Copper")                                //返回<ore:ingotCopper>
Utils.ore(OrePrefix.getPrefix("ingot"), <material:copper>)  //返回<ore:ingotCopper>
Utils.ore("ingot", <material:copper>)                       //返回<ore:ingotCopper>
Utils.ore(OrePrefix.getPrefix("ingot"), "copper")           //返回<ore:ingotCopper>
Utils.fluid("copper")             // <liquid:copper>
Utils.fluid(<material:copper>)    // copper这个集中的流体类,返回(<liquid:copper>)
Utils.metaitem("some.item")       // <metaitem:some.item>
Utils.material("copper")          // <material:copper>

Multiblock Tweaker

我们现在来讲讲MBT,但是只涉及他的自定义多方块GT机器部分。


首先来看看官方给出的实例

//不需要预加载器-#loader.
import mods.gregtech.multiblock.Builder;
import mods.gregtech.multiblock.FactoryBlockPattern;
import mods.gregtech.multiblock.RelativeDirection;
import mods.gregtech.multiblock.IBlockMatcher;
import mods.gregtech.multiblock.MultiblockAbility;
import mods.gregtech.multiblock.FactoryMultiblockShapeInfo;
import mods.gregtech.multiblock.IBlockInfo;
import mods.gregtech.MetaTileEntities;
import mods.gregtech.recipe.RecipeMap;
import crafttweaker.world.IFacing;
import crafttweaker.text.ITextComponent;

var loc = "multiblock_alloy_smelter";  //赋值写法,"里边的字符串是机器名称
var meta = 2000; //meta id 不能冲突,如果出现了问题,crafttweaker会在log中抛出一个警告。
Builder.start(loc, meta)  //这里填的是俩个变量,其实可以填 ("string name" int id)
    .withPattern(  //模版
        FactoryBlockPattern.start(RelativeDirection.RIGHT, RelativeDirection.DOWN, RelativeDirection.FRONT)  //相对方向,分别是右,下,前。
            .aisle(  //层级写法
                "CCC",
                "CCC",
                "CSC")
            .aisle(
                "CCC",
                "C C",  //空格代表无方块
                "CCC")
            .aisle(
                "CCC",
                "CCC",
                "CCC")
            .whereOr("C",  //解释器
                <metastate:gregtech:metal_casing:2>,  也就是结构中C的部分代表meta值为2的金属机械方块
                IBlockMatcher.abilityPartPredicate(  //方块匹配器,方块部件谓语
                    MultiblockAbility.INPUT_ENERGY,  //谓语写在C部分下方,代表这个谓语属于C方块
                    MultiblockAbility.IMPORT_ITEMS,  //具体功能则是这些部件可以替换C方块
                    MultiblockAbility.EXPORT_ITEMS
                ))
            .where("S", IBlockMatcher.controller(loc))  //解释结构内S代表的匹配器匹配控制器,然后输入loc/多方块机械名称,代表的就是此机器独特注册的控制器。
            .where(" ", IBlockMatcher.ANY)  //解释结构内空格代表的匹配器匹配任何方块。
            .build())  //构建
    .addDesign(  //添加设计
        FactoryMultiblockShapeInfo.start()  //多方块工厂,结构信息,开始匹配
            .aisle(
                "ICC",
                "CCC",
                "CCC")
            .aisle(
                "SCC",
                "E C",
                "CCC")
            .aisle(
                "OCC",
                "CCC",
                "CCC")
            .where("C", <metastate:gregtech:metal_casing:2>)  //解释器,不做叙述
            .where("S", IBlockInfo.controller(loc))  //控制器
            .where("I", MetaTileEntities.ITEM_IMPORT_BUS[0], IFacing.west())  //解释器,meta方块,物品输入总线,IFacing面朝西
            .where("O", MetaTileEntities.ITEM_EXPORT_BUS[0], IFacing.west())  //物品输出总线
            .where("E", MetaTileEntities.ENERGY_INPUT_HATCH[2], IFacing.west())  //能量输入仓
            .where(" ", IBlockInfo.EMPTY)  //解释器,方块信息,代表空方块
            .build())
    .withPartTooltip(<gregtech:metal_casing:2>, ITextComponent.fromString("Example") as ITextComponent)  //部件Tooltip,会在显示多方块介绍时,出现在金属机械外壳的Tooltip里,使用的ITextComponent
    .withRecipeMap(  //使用的RecipeMap
        RecipeMap.getByName("alloy_smelter")) //只要使用一个已经存在的RecipeMap就可以了,建议搭配我们的自定义MBTRecipeMap使用。
    .buildAndRegister();  //构建并且注册

   //下面这些最好在.lang文件中指定,因为这些文件可能无法正常工作。
game.setLocalization(  //设置本地化名,lang key
    "multiblocktweaker.machine.multiblock_alloy_smelter.name",
    //"Multiblock Alloy Smelter" 请把上面的lang key放在.lang文件里,下面会讲
);
game.setLocalization(  //设置
    "multiblocktweaker.multiblock.multiblock_alloy_smelter.description",
    //"The Multiblock Alloy Smelter is a multiblock that does alloy smelter recipes. Hello, world!"
);
//别忘了给机器添加配方
recipes.addShaped(
    <gregtech:machine:2000>,
    [
        [<gregtech:cable:71>,         <gregtech:meta_item_2:32487>,         <gregtech:cable:71>],
        [<gregtech:meta_item_2:32487>, <gregtech:metal_casing:2>,  <gregtech:meta_item_2:32487>],
        [<gregtech:cable:71>,         <gregtech:meta_item_2:32487>,         <gregtech:cable:71>]
    ]
);

还不懂嘛?来看看第二个实例

//不需要预加载器。
import mods.gregtech.multiblock.Builder;
import mods.gregtech.multiblock.FactoryBlockPattern;
import mods.gregtech.multiblock.RelativeDirection;
import mods.gregtech.multiblock.IBlockMatcher;
import mods.gregtech.multiblock.MultiblockAbility;
import mods.gregtech.multiblock.FactoryMultiblockShapeInfo;
import mods.gregtech.multiblock.IBlockInfo;
import mods.gregtech.MetaTileEntities;
import mods.gregtech.recipe.FactoryRecipeMap;
import crafttweaker.world.IFacing;

var loc = "magic_miner";  //依旧是loc
var meta = 2001; //记住,id不能冲突,不然crafttweaker要抛错误
var magic_miner = Builder.start(loc, meta)  //构建开始
    .withPattern(
        FactoryBlockPattern.start(RelativeDirection.RIGHT, RelativeDirection.DOWN, RelativeDirection.FRONT)  //相对位置
            .aisle(
                "CCC",
                "CCC",
                "CSC")
            .aisle(
                "CCC",
                "C C",
                "CCC")
            .aisle(
                "CCC",
                "CCC",
                "CCC")
            .whereOr("C",
                <metastate:gregtech:metal_casing:3>,  //解释器,解释此结构中的C代表meta值3的金属机械外壳。
                IBlockMatcher.abilityPartPredicate(  //方块匹配器,特殊谓词
                    MultiblockAbility.INPUT_ENERGY,  //能量输入
                    MultiblockAbility.IMPORT_ITEMS,  //输入物品
                    MultiblockAbility.IMPORT_FLUIDS,  //输入流体
                    MultiblockAbility.EXPORT_ITEMS  //输出物品
                ))
            .where("S", IBlockMatcher.controller(loc))  //解释器,匹配controller的方块,括号里边填的是机器名称,或者是loc变量名。
            .where(" ", IBlockMatcher.ANY)  //解释空格可以是任何方块
            .build())  //构建
    .addDesign(  //增加设计
        FactoryMultiblockShapeInfo.start()  //结构信息
            .aisle(
                "ICC",
                "CCC",
                "ECC")
            .aisle(
                "SCC",
                "C C",
                "CCC")
            .aisle(
                "OCC",
                "CCC",
                "FCC")
            .where("C", <metastate:gregtech:metal_casing:3>)  //解释器
            .where("S", IBlockInfo.controller(loc))  //解释器
            .where("I", MetaTileEntities.ITEM_IMPORT_BUS[0], IFacing.west())  //解释器,物品输入总线
            .where("F", MetaTileEntities.FLUID_IMPORT_HATCH[0], IFacing.west())  //解释器,流体输入仓
            .where("O", MetaTileEntities.ITEM_EXPORT_BUS[0], IFacing.west())  //物品输出总线
            .where("E", MetaTileEntities.ENERGY_INPUT_HATCH[2], IFacing.west())  //能量输入仓
            .where(" ", IBlockInfo.EMPTY)  //空方块
            .build())  //构建
    .clearTooltips(true)  //清除所有Tooltip
    .withRecipeMap(  //自定义RecipeMap
        FactoryRecipeMap.start(loc)
    .minInputs(1)   //最小输入
    .maxInputs(1)  //最大输入
    .minOutputs(3)  //最小输出
    .maxOutputs(27)  //最大输出
    .maxFluidInputs(1)  //最大流体输入
    .build())  //构建
    .buildAndRegister();  //构建和注册



//别忘了机器控制器的配方
recipes.addShaped(
    <gregtech:machine:2001>,
    [
        [<gregtech:cable:71>,         <gregtech:meta_item_2:32487>,         <gregtech:cable:71>],
        [<gregtech:meta_item_2:32487>, <gregtech:metal_casing:3>,  <gregtech:meta_item_2:32487>],
        [<gregtech:cable:71>,         <gregtech:meta_item_2:32487>,         <gregtech:cable:71>]
    ]
);
<multiblock:multiblocktweaker:magic_miner> // The Bracket Handler can also be used to refer to it
    .recipeMap  //RecipeMap
.recipeBuilder()
    .duration(500)  //时间
    .EUt(500)  //输入EU/tick
    .inputs(<minecraft:chest>)  //输入物品
    .fluidInputs(<liquid:water> * 8000)  //输入液体,8000mB水
    .outputs(<gregtech:ore_cassiterite_0:3> * 64,  //物品输出
             <gregtech:ore_redstone_0> * 64,
             <gregtech:ore_nickel_0> * 64,
             <gregtech:ore_rutile_0> * 64,
             <gregtech:ore_rutile_0> * 64,
             <gregtech:ore_uraninite_0:3> * 64,
             <gregtech:ore_galena_0> * 64,
             <gregtech:ore_galena_0> * 64,
             <gregtech:ore_salt_0> * 64)
    .buildAndRegister();  //构建和注册

GTCEu版MBT

GTCEu版MBT是由KailBash,eutro,brachy等人开发的,版本号大于2.0.0。

先来看第一个例子:

//不需要#loader gregtech

import mods.gregtech.multiblock.Builder;
import mods.gregtech.multiblock.FactoryBlockPattern;
import mods.gregtech.multiblock.RelativeDirection;
import mods.gregtech.multiblock.functions.IPatternBuilderFunction;
import mods.gregtech.IControllerTile;
import mods.gregtech.multiblock.CTPredicate;
import mods.gregtech.multiblock.IBlockPattern;
import mods.gregtech.recipe.RecipeMap;

var loc = "glass_smelter";
Builder.start(loc) //现在!我们不需要去记那Fu*k的ID了!现在他会自己补上!
    .withPattern(function(controller as IControllerTile) as IBlockPattern {  //function
                                    //CTPredicate.states()制定了方块,而下方的setMinGlobalLimited(15)指的是这个结构中只能有15个此方块,tooltip现在可以使用lang key了
                       val glass = CTPredicate.states(<blockstate:minecraft:glass>).setMinGlobalLimited(15).addTooltips("glass_smelter.glass.tooltips");
                       return FactoryBlockPattern.start(RelativeDirection.RIGHT, RelativeDirection.DOWN, RelativeDirection.FRONT)
                           .aisle(
                               "KKK",
                               "KKK",
                               "KSK",
                               "D D")
                           .aisle(
                               "CCC",
                               "CLC",
                               "CCC",
                               "   ").setRepeatable(2)  //这个setter的意思是被标记的这一层重复叠加俩次。
                           .aisle(
                               "CCC",
                               "CCC",
                               "CCC",
                               "D D")
                           .where("K", glass | controller.autoAbilities()) //等于glass.or(controller.autoAbilities())
                           .where("C", glass)  //玻璃
                           .where("L", CTPredicate.liquids(<liquid:lava>)) //等于CTPredicate.blocks(<block:minecraft:lava>) == CTPredicate.states(<blockstate:minecraft:lava>) == <blockstate:minecraft:lava>
                           .where("S", controller.self())  //意思是此机器的控制器
                           .where("D", <blockstate:minecraft:fence>)  //栅栏方块
                           .where(" ", CTPredicate.getAny())  //代表任何方块
                           .build();  //构建
                 } as IPatternBuilderFunction)  //function
    .withRecipeMap(RecipeMap.getByName("bender")) //可以使用已有的RecipeMap和自定义RecipeMap,这里的代码等于<recipemap:bender>
    .withBaseTexture(<blockstate:minecraft:glass>)  //机器的基础贴图设为玻璃
    .buildAndRegister();  //构建并注册

 //下面的东西建议放在.lang文件里


//别忘了加入控制器的配方和机器的RecipeMap。

再来看看第二个例子:

//不需要#loader

import mods.gregtech.multiblock.Builder;
import mods.gregtech.multiblock.FactoryBlockPattern;
import mods.gregtech.multiblock.RelativeDirection;
import mods.gregtech.multiblock.functions.IPatternBuilderFunction;
import mods.gregtech.IControllerTile;
import mods.gregtech.multiblock.CTPredicate;
import mods.gregtech.multiblock.IBlockPattern;
import mods.gregtech.recipe.FactoryRecipeMap;
import mods.gregtech.recipe.RecipeMap;

var loc = "mbt:magic_miner";  //赋值loc
val magic_miner = Builder.start(loc) //自动补全ID
    .withPattern(function(controller as IControllerTile) as IBlockPattern {
                       return FactoryBlockPattern.start()  //模版
                          .aisle("CCC", "CCC", "CCC")  //二维数组的另一种写法
                          .aisle("CCC", "C C", "CMC")
                          .aisle("CSC", "CCC", "CCC")
                          .where('S', controller.self())  //控制器
                          .where("C", CTPredicate.states(<metastate:gregtech:metal_casing:3>)//meta值为3的金属机器外壳
                                      | CTPredicate.abilities(<mte_ability:IMPORT_ITEMS>).setMinGlobalLimited(1).setPreviewCount(1) //至少有一条IMPORT_ITEMS总线。JEI预览显示只有一条。
                                      | CTPredicate.abilities(<mte_ability:EXPORT_ITEMS>).setMinGlobalLimited(1).setPreviewCount(1)
                                      | CTPredicate.abilities(<mte_ability:IMPORT_FLUIDS>).setMinGlobalLimited(1).setPreviewCount(1)
                                      | CTPredicate.abilities(<mte_ability:INPUT_ENERGY>).setMinGlobalLimited(1).setMaxGlobalLimited(3).setPreviewCount(1) //那里至少有一个INPUT_ENERGY仓. JEI只会显示一个
                          )
                                      
                          .where('M', controller.autoAbilities(false, false, false, false, false, false, true)) //等于CTPredicate.abilities(<mte_ability:MUFFLER_HATCH>)
                          .where(' ', CTPredicate.getAir())  //空气
                          .build();  //构建
                 } as IPatternBuilderFunction)
    .withRecipeMap(  //RecipeMap
        FactoryRecipeMap.start("magic_miner") //新建一个RecipeMap.
            .minInputs(1)
            .maxInputs(1)
            .minOutputs(3)
            .maxOutputs(27)
            .maxFluidInputs(1)
            .build())  //构建
    .withBaseTexture(<cube_renderer:FROST_PROOF_CASING>) //在CEu中寻找现有的渲染器。但是,你也可以在这里使用<metastate:gregtech:metal_casing:3>。
    .buildAndRegister();  //构建和注册
//设置properties
magic_miner.hasMaintenanceMechanics = false;  //维护机器,维护仓
magic_miner.hasMufflerMechanics = true;  //消音,消音仓
magic_miner.frontOverlay = <cube_renderer:COKE_OVEN_OVERLAY>;  //前面材质
//添加一个简单的RecipeMap
magic_miner //括号处理程序也可以用来指代它。(<multiblock:mbt:magic_miner>)
    .recipeMap //关于如何添加配方的细节,请参考CEu Wiki。
.recipeBuilder()
    .duration(500)  //时间
    .EUt(500)  //EU/tick
    .inputs(<minecraft:chest>)  //输入
    .fluidInputs(<liquid:water> * 8000)  //输入流体
    .outputs(<gregtech:ore_cassiterite_0:3> * 64,  //输出
             <gregtech:ore_redstone_0> * 64,
             <gregtech:ore_nickel_0> * 64,
             <gregtech:ore_uraninite_0:3> * 64,
             <gregtech:ore_galena_0> * 64,
             <gregtech:ore_galena_0> * 64,
             <gregtech:ore_salt_0> * 64)
    .buildAndRegister();  //构建和注册

 //这些最好在.lang文件中指定,因为这些文件可能无法正常工作。
game.setLocalization(
    "mbt.machine.magic_miner.name",
    "Magic Miner"
);
game.setLocalization(
    "mbt.multiblock.multiblock_dt.description",
    "The Magic Miner is a multiblock that mines ores from nothing."
);
game.setLocalization(
    "recipemap.magic_miner.name",
    "Magic Miner"
);
//别忘了加配方。

GTCEuWiki

这一部分是在GitHub里GTCEu的仓库发现的,他的Wiki页面中还有一部分GTCEu专用的魔改。


GTCEu的材料部分

介绍

你应该先来了解一下

“材料”应该是什么?

---

材料是CEu的基础,他定义了一种属性及其属性,材料通常以元素(例如铁Fe,氧O)或化合物(例如水H2O),但他也有一些奇怪的材料,例如末影之眼下界之星


定义了哪些属性?

---

材质指定它是否具有流体属性、等离子属性、粉属性、宝石属性或锭属性。当它具有特定属性时,GTCEu 会自动注册相应的项目或流体。有些属性需要其他属性,例如Ingot需要Dust等。


他还定义了什么?

---

您还可以定义颜色、标志(特定属性的指示符)、MaterialIconSet(纹理)、CableProperties、元素、公式(工具提示)、组件等。别担心,它们并不复杂,下面会详细介绍。


搜索现有材料

官方给出了两种办法:一种简单的,一种不简单的。(?)


简单的办法

此方法要求材质首先存在。此方法脱离材料的Unlocalized Name,这是在 lang 文件中翻译之前使用的名称。您可以使用/gt hand命令来检索它,它在聊天中的“Material”一词之后列出。它的工作原理几乎完全一样/ct hand,但专门为 GregTech 项目做了额外的事情。

//导入包Material类来使用Materials
import mods.gregtech.material.Material;

//将变量my_material赋值给一个名为Steel的材料。
var my_material = <material:steel>;

至于难的办法,和之前的办法一样,因为效果相同,而第一个更加简单,所以我不想讲第二种。


创建新材料


首先,不能用任何普通的 CraftTweaker 脚本制作材料,他们需要一个特定的预加载器,#loader gregtech,这必须在文件的第一行,这是材料生成工作所必需的。请注意,其他常规脚本(例如添加制作表配方)在带有此加载程序的脚本中不起作用!


材料是使用MaterialBuilder来注册的. 如果您使用 GregTech CE 和 CEu 的系统添加机器配方,这将感觉非常相似。


所有材料都需要一个数字id和一个名称。id必须在 0-32768 之间,但是,ID 32000+ 保留给 Crafttweaker 。任何附加开发人员都不应该使用该范围内的 id,因此不必担心冲突。700 多种材料也超过了 CEu 本身使用的所有材料!name必须全部小写,不包含空格,并且不包含特殊字符(@、% 等)。

#loader gregtech
//导入包MaterialBuilder和Material两个类来使用材料注册
import mods.gregtech.material.MaterialBuilder;
import mods.gregtech.material.Material;

/* 
 * 这本身并没有什么作用。
 *它为一个新的MaterialBuilder分配了一个变量,其id为32000,名称为 "my_material"。
 */ 
 
var my_material_builder = MaterialBuilder(32000, "my_material");
#loader gregtech

//导入包MaterialBuilder和Material两个类来新建材料
import mods.gregtech.material.MaterialBuilder;
import mods.gregtech.material.Material;

/* 
 *这还不能完全工作。继续阅读,了解这还需要什么!
 *它将为一个新的材料分配一个变量,其id为32001,名称为 "my_real_material"
 * 
 *使用.build();来完成一个材料的构建。这就是实际创建它的过程
 */ 
 
var my_material = MaterialBuilder(32001, "my_real_material").build();

添加材料属性

这是材料真正开始成形的地方。以下所有方法都在MaterialBuilder(). 向下看,到第一个示例以查看如何使用此方法,每种方法都可以一个接一个地链接在一起,直到指定所需的材料。然后它被构建并结束并返回一个完成的材料。


流体

fluid(@Optional String type, @Optional boolean hasBlock)

FluidProperty向该材料添加一个流体。以上两个参数是可选的,如果要修改后面的可选参数,则必须为前面的每个参数指定一个值。请注意,使用fluid().

String type的type有两种,分别为"fluid"和"gas"。

hasBlock 如果为true,则会为这个流体注册一个流体块,可以放在世界中。


等离子体

plasma()

PlasmaProperty为该材料生成等离子体,它不需要FluidProperty,但可以与它一起使用。


材料:

dust(@Optional int harvestLevel, @Optional int burnTime)

向此材料添加一个DustProperty,它会生成粉、小撮粉和小堆粉,自动应用于IngotProperty和GemProperty。

harvestLevel 开采时材料方块的挖掘等级,如果材料也有ToolProperty,该值也将用于确定工具的采掘等级。

burnTime 该材料作为熔炉燃料的燃烧时间(以tick为单位),如果未指定,则该材料不能用作熔炉燃料。


材料:

ingot(@Optional int harvestLevel, @Optional int burnTime)

向此材料添加一个IngotProperty,生成一个锭材料,它会自动添加一个DustProperty,不兼容GemProperty。


harvestLevel 开采时材料方块的挖掘等级,如果材质也有ToolProperty,该值也将用于确定工具的采掘等级,如果此材料已经定义了挖掘级别,它将被覆盖。

burnTime 该材料作为熔炉燃料的燃烧时间(以tick为单位),如果未指定,该材料不能用作熔炉燃料。如果此材料已经定义了燃烧时间,它将被覆盖。


宝石

gem(@Optional int harvestLevel, @Optional int burnTime)

向此材料添加一个GemProperty,生成所有类型的格雷科技宝石,它会自动添加一个DustProperty,不兼容IngotProperty。

harvestLevel 收获等级

burnTime 作为燃料的燃烧时间(单位tick)


颜色

color(int color)

设置此材质的颜色,不使用默认为 0xFFFFFF 

colorAverage(),颜色可以以下任一格式提供:整数值,例如16777215或0xFFFFFF。这是 RGB,没有 Alpha 通道。


颜色平均:colorAverage()

材质的颜色将是材质中所有组件颜色的加权平均值。


工具

toolStats(float speed, float damage, int durability, int enchantability)

设置由该材料制成的工具的统计数据。

  • speed- 工具的挖掘速度。

  • damage- 工具的攻击伤害。

  • durability- 工具的耐久。

  • enchantability- 工具的附魔能力。


高炉温度

blastTemp(int temp, @Optional String gasTier, @Optional int eutOverride, @Optional int durationOverride)

设置此材料的高炉温度,如果低于 1000 开尔文,还会添加土高炉配方,如果高于 1750 开尔文,将添加电力高炉,热材料锭及其真空冷冻机配方,如果具有此属性的材料具有流体,将会使流体温度变为这里设置的温度。

  • temp- 加热材料所需的开尔文高炉温度。就像在现实生活中一样,这个值不能小于0。

  • @Optional gasTier- 用于在电动高炉中熔炼的气体层。可用选项有:"LOW", "MID", "HIGH", "HIGHER", "HIGHEST.

  • @Optional eutOverride- 将自动生成的电力高炉配方的 EU/t 设置为指定值。

  • @Optional durationOverride- 将自动生成的电力高炉配方的时间设置为指定值。


矿石

ore(@Optional int oreMultiplier, @Optional int byproductMultiplier, @Optional boolean emissive)

为该材料添加矿石、粉碎矿石、纯净的粉碎矿石、粉碎离心矿石、肮脏的粉和纯净的粉。


  • @Optional oreMultiplier- 矿石的破碎矿石 -> 研磨破碎矿石的产出乘数,默认值:1(无乘数)。

  • @Optional byproductMultiplier- 任何类型的碎矿石加工的副产品输出量乘数,默认值:1(无乘数)。

  • @Optional emissive- 矿石是否应该使用发光材质(见下图)。默认值:false。

GT魔改入门作者必读-GTCE和CEu的部分魔改教程-第一期-第1张图片矿石发光


清洗

washedIn(Material material, @Optional int washedAmount)

设置使用化学方式清洗材料的粉碎矿石的内容,需要OreProperty设置。

  • material- 洗涤该材料的流体材料,需要的材料为FluidProperty。

  • @Optional washedAmount- 破碎矿石被冲洗需要的流体量,默认值:100mB。


电磁选矿

separatedInto(Material... materials)

将处理此材料的干净的矿石粉的产物设置在电磁选矿机中,需要OreProperty设置。

  • materials- 在电磁选矿机中输出的一系列材料,这已经自动包含了材料本身。


矿石

addOreByproducts(Material... materials)

设置此材质的矿石副产品,需要OreProperty设置。

  • materials- 用于各种矿石加工时副产品输出的一系列副产材料。


矿石熔炼

oreSmeltInto(Material material)

将直接熔炼矿石的产物设置在此材料的矿石相关形式的原版熔炉设置中,需要OreProperty设置。

  • material- 在熔炉中输出的材料。可以是IngotProperty,GemProperty或DustProperty。


磁化

polarizesInto(Material material)

设置此材料的磁化器产物,需要IngotProperty设置。

  • material- 磁化时输出的材料。


电弧炉熔炼

arcSmeltInto(Material material)

设置此材料的电弧炉产物。需要IngotProperty设置。

  • material- 电弧炉输出的材料,默认情况下,材料将输出本身。


研磨

macerateInto(Material material)

设置此材料的研磨机产物,需要IngotProperty设置。

  • material- 研磨时输出的材料,默认情况下,材料将输出本身。


流体温度

fluidTemp(int temp)

设置此材料的流体的温度,需要FluidProperty设置。

  • temp- 要设置的开尔文温度,就像在现实生活中一样,这个值不能小于0。


电线/线缆

cableProperties(long voltage, int amperage, int loss, @Optional boolean isSuperCon)

添加此材料的电线和线缆,需要IngotProperty设置。

  • voltage- 1x 电线/线缆可以传输的电压级别。

  • amperage- 1x 电线/线缆可以传输的电流量,安培。

  • loss- 1x线每块的损失(线损),电缆具有此值除以 2,但除非设置为0 ,否则永远不会是无损的电线。

  • @Optional isSuperCon- 这是否是超导体?这将禁止电缆产生,并使线损为0。


流体管道

fluidPipeProperties(int maxTemp, int throughput, boolean gasProof)

添加此材料的流体管道,需要IngotProperty设置。

  • maxTemp- 设置管道中允许的最高流体温度。当要传输流体超过这个温度时,管道会燃烧并且消失。

  • throughput- 设置通过管道的最大流量,乘以这个值20就是 Tiny Pipe 的最大速率,每个后续管道都进一步乘2以前一个管道。

  • gasProof- 如果true,则管道能够传输气体状态的流体。否则,传输的气体会蒸发。



物品管道

itemPipeProperties(int priority, float stacksPerSec)

添加此材料的物品管道,需要IngotProperty设置。

  • priority- 设置管道的优先级,物品将采用优先级最低的路径,该值直接用于正常管道,小管道的这个值乘以1.5,大管道的这个值乘以0.75。

  • stacksPerSec- 设置每秒 64 个项目的最大传输速率,该值直接用于正常管道,小管道的这个值乘以0.5,大型管道的这个值乘以2。



默认附魔


addDefaultEnchant(IEnchantment enchantment)

向此材料添加默认附魔,需要ToolProperty设置,将直接将这些附魔应用于此材料的工具。

  • enchantment - 要设置的附魔,使用 CraftTweaker 的IEnchantment


例子

#loader gregtech

import mods.gregtech.material.MaterialBuilder;
import mods.gregtech.material.Material;

var specialSteel = MaterialBuilder(32002, "special_steel") //名称
    .fluid("gas", false) //gas
    .ingot() //启用锭材料,故开启粉材料
    .color(0x0000FF) //纯蓝色
    .toolStats(10, 3, 256, 21) //工具设置
    .blastTemp(2900) //高炉温度,大于1000使用EBF电力高炉
    .ore() //启用矿石
    .addOreByproducts(<material:gold>, <material:copper>) //矿石副产
    .cableProperties(128, 2, 4, false) //添加线缆
    .build(); //构建此材料

怎么样,是不是比GTCE的简单多了。



改变材料外观

你可能已经从上面的示例中注意到,材质的纹理看起来与许多其他材质的样式相同,这称为MaterialIconSet,也就是材质集,整个下一节都致力于解释这个系统以及如何使用它。


可用的MaterialIconSet材质集有:"NONE", "METALLIC", "DULL", "MAGNETIC", "QUARTZ", "DIAMOND", "EMERALD", "SHINY", "ROUGH", "FINE", "SAND", "FLINT", "RUBY",  "LAPIS", "FLUID", "GAS", "LIGNITE", "OPAL", "GLASS", "WOOD", "GEM_HORIZONTAL", "GEM_VERTICAL", "PAPER", "NETHERSTAR", "BRIGHT",这些材质集被放在mod文件/assets/textures/items/materials_set里,每个子文件夹代表每个材质集,你可以打开文件夹来看看材质集内部包含哪些材质模版。


例如,下图显示了具有不同 MaterialIconSet 的矿石的外观。

GT魔改入门作者必读-GTCE和CEu的部分魔改教程-第一期-第2张图片

确定其所有材质的 MaterialIconSet 使用单一方法设置。



iconSet:iconSet(String iconSet)

设置MaterialIconSet至此材质的,默认值取决于材质是否具有以下几点:

  • GemProperty- 默认为"GEM_VERTICAL"

  • IngotProperty或DustProperty- 默认为"DULL"

  • FluidProperty- 默认为"FLUID"或"GAS",取决于FluidType

  • PlasmaProperty- 默认为"FLUID",但无论如何都会收到特殊的等离子流材质。

默认值将按此顺序确定。


使用材质集的实例:

#loader gregtech
import mods.gregtech.material.MaterialBuilder;
import mods.gregtech.material.Material;

var specialSteelTextured = MaterialBuilder(32003, "special_steel_textured") // name
    .fluid("gas", false) //气体和流体方块
    .ingot() //锭粉材料
    .color(0x0000FF) //纯蓝
    .iconSet("shiny") //材质集设置为 shiny
    .toolStats(10, 3, 256, 21) //工具
    .blastTemp(2900) //高炉温度
    .ore() //矿石
    .addOreByproducts(<material:gold>, <material:copper>) //副产物
    .cableProperties(128, 2, 4, false) //线缆
    .build(); //构建

成分

成分是指材料的组成成分。


例如,钨钢的成分是1 Tungsten (W)和1 Steel (Fe)。这是它的化学式:

GT魔改入门作者必读-GTCE和CEu的部分魔改教程-第一期-第3张图片


MaterialStack

组件是通过一个叫做MaterialStack的东西来确定的。这是ItemStack的材料版本,ItemStack是一个有计数的项目。在我们的例子中,MaterialStack是一个带有计数的材料,创建MaterialStack很容易,但由于CraftTweaker的编译系统,你必须确保在*的前后都有空格,否则你可能会收到一个脚本错误。

import mods.gregtech.material.Material;

//创建一个材料锡的MaterialStack,计数为3。

var my_material_stack = <material:tin> * 3;

很简单,不是吗?


那么这一篇就先到这里了罢,剩下的下期再来吧。

补上下期链接