一、添加容器布局、小格子背景、放置限制
想要在模组中添加一个带有容器的物体,不论是建筑、背包还是武器、盔甲,都需要先给他定义一个专属的容器布局。如果在原版中的容器布局有符合你的需求的,也可以直接使用原版的容器布局,跳过这一步。
容器布局需要添加在modmain中,当然如果有需要让modmain界面简洁,可以在modmain中添加一行代码,以将代码转移至其他文件中:
modimport("scripts/mod_containers") --模组容器布局定义文件
添加容器的代码如下:
-- 向官方的params表中添加容器布局
local containers = require "containers"
local params = containers.params
-- 设置你所需要的布局
params.sakura_backpack =
{
widget =
{
slotpos = {}, -- 格子位置
slotbg = -- 格子背景图片,使用原版的话设为nil或者直接用空表即可
{nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,
{ image = "preparedfood_slot.tex", atlas = "images/hud2.xml" },
{ image = "preparedfood_slot.tex", atlas = "images/hud2.xml" },
{ image = "preparedfood_slot.tex", atlas = "images/hud2.xml" },
{ image = "preparedfood_slot.tex", atlas = "images/hud2.xml" }} ,
animbank = "ui_krampusbag_2x8", -- 设置容器背景板
animbuild = "ui_krampusbag_2x8", -- 这里用的坎普斯背包的
pos = Vector3(-5, -100, 0), -- 设置容器的总体位置,以人物为原点(0,0),左上为正,右下为负
},
issidewidget = true, -- 是否为侧边小部件,如果为true则是,原点位置变为右侧中心,左上为正,右下为负
type = "pack", -- 容器类型,可以随意设置
openlimit = 1, -- 同一类型的容器的打开个数限制
}
-- 设置容器每个小格子的位置
for y = 0, 6 do
for x=0,1 do
table.insert(params.sakura_backpack.widget.slotpos, Vector3(-162 + 75 * x, -75 * y + 240, 0))
end
end
-- 设置每个格子可以放什么物品,返回值为true则可以防止,false则不能
-- 格子编号slot从1开始,从左上到右下逐个+1
function params.sakura_backpack.itemtestfn(container, item, slot)
-- 这些格子只能放置带有"preparedfood"的实体
if (slot>=9)and(slot<=16) then
return item:HasTag("preparedfood")
end
-- 其他格子什么都能放
return true
end
此时,容器布局、容器内每个小格子的背景、每个小格子放置物品种类的限制已经写好。
二、更新最大小格子数量
如果你新增的容器的小格子数量大于原版的容器最大小格子数量(坎普斯背包,14个),则需要更新最大小格子数量,否则会在插入slotpos{}时报错,更新代码如下:
-- 更新最大小格子数量数
for k, v in pairs(params) do
containers.MAXITEMSLOTS = math.max(containers.MAXITEMSLOTS, v.widget.slotpos ~= nil and #v.widget.slotpos or 0)
end
这段代码也应放入modmain中
三、添加container组件
为需要容器的实体添加容器组件,在实体创建函数fn()中为其添加即可:
-- 添加容器组件
inst:AddComponent("container")
-- 设置使用的容器布局(步骤一中设置的)
inst.components.container:WidgetSetup("sakura_backpack2")
如有需要,还可选择添加容器打开、关闭时的任务:
inst.components.container.onopenfn = onopen -- 设置打开容器时的回调函数
inst.components.container.onclosefn = onclose -- 设置关闭容器时的回调函数
-- onopen、onclose为函数,在其内写任务代码即可
此外,还有一些常用container方法,可在官方代码“container.lua”中了解,如:
inst.components.container.skipclosesnd = true -- 跳过关闭声音
inst.components.container.skipopensnd = true -- 跳过打开声音
inst.components.container:EnableInfiniteStackSize(true) -- 启用无限堆叠大小
四、获取容器小格子中的物品信息
4.1 获取小格子中的实体信息
我们可以通过下面的语句获得容器内的小格子中实体的信息:
local item = {}
item[i] = inst.components.container.slots[i]
此时item[i]对应的是小格子内堆叠后的实体。
4.2 堆叠组件用法
有了小格子内的实体item[i]后,就可以进行玩法开发了:
我们可以通过stackable组件获得堆叠实体的堆叠数量:
if item[i].components.stackable ~= nil and item[i].components.stackable:StackSize() ~= nil then -- 先判断是否可堆叠,堆叠数量是否为空,避免报错
item_stacksize[i] = item[i].components.stackable:StackSize()
end
注意:StakeSize()至少为1,若需彻底移除则需要Remove()语句,具体使用在3.3中。
4.3 其他技巧
此时我们可以从改变堆叠数量上进行玩法开发,比如对于容器内的物品,每分钟移除一个,并返还一个蓝宝石。
先说明给予玩家或者物体某个物品的方法:
-- 给予玩家
player.components.inventory:GiveItem(SpawnPrefab("bluegem",2)
-- 给予容器
inst.components.container:GiveItem(SpawnPrefab("ash"),1)
以及移除某个实体的方法:
inst:Remove()
还有“每隔2秒”如何实现:
inst:DoPeiodicTask(2,function() -- "2"为间隔,单位是秒,此外还可以用宏定义的帧:FRAMES*n
end)
现在我们就可以完成上面的玩法设想:
inst:DoPeriodicTask(2, function()
for i = 1,14 do
item[i] = inst.components.container.slots[i]
end
for i = 1,14 do
if item[i] ~= nil and item[i].components.stackable ~= nil and item[i].components.stackable:StackSize() ~= nil then
if item[i].components.stackable:StackSize() > 1 then --堆叠数至少为1,想要清空只能Remove()
item[i].components.stackable:SetStackSize(item[i].components.stackable:StackSize() - 1)
else
item[i]:Remove()
end
inst.components.container:GiveItem(SpawnPrefab("bluegem"),1)
end
end
end)
4.4 其他玩法设计思路
我们现在已经可以对容器中的每个小格子做分别处理,通过对每个小格子中的堆叠物品做不同的处理,我们可以开发出更多的玩法。(如:棱镜_靠背熊)
我在即将上线的模组《丰耘秘境》(预计9.17上线,可能会因模组进一步完善而推迟)中也为背包添加了许多不同的玩法,大家敬请期待吧。