写一个烹煮锅


构思:

设计一个和饥荒的cookpot类似的烹煮锅
作为一个建筑应该有配方、建造时的动画等
作为一个烹煮锅额外有烹煮动画,有容器,容器背景图,有烹煮按钮,(由于饥荒的cookpot容量是4格的,那我这里做一个3格的)
有自己的几套料理配方,烹煮成功后,产品直接给到容器里,需要玩家打开拿取(这里就没有做烹煮成功,料理摆在锅子上的动画)




  1. 创建一个建筑类预制物
  1require "prefabutil"
2local assets =
3{
4    Asset("ANIM""anim/sollypot.zip"),
5    Asset("ANIM""anim/ui_sollypot_1x3.zip"),
6    Asset("ATLAS""images/inventoryimages/sollypot.xml")
7}
8local function onhammered(inst, worker)  --被锤子锤爆时的function (可以直接照搬)
9    --如果有可燃烧组件并且在燃烧则执行灭火
10    if inst.components.burnable ~= nil and inst.components.burnable:IsBurning() then 
11        inst.components.burnable:Extinguish()
12    end
13    --锤爆掉落物品
14    inst.components.lootdropper:DropLoot()
15    if inst.components.container ~= nil then
16        inst.components.container:DropEverything()
17    end
18    local fx = SpawnPrefab("collapse_small")
19    fx.Transform:SetPosition(inst.Transform:GetWorldPosition())
20    fx:SetMaterial("wood")
21    inst:Remove()
22end
23
24local function onhit(inst, worker) --被锤子锤时的fn (可以直接照搬)
25    if not inst:HasTag("burnt"then
26        inst.AnimState:PlayAnimation("hit_idle_loop")
27        inst.AnimState:PushAnimation("idle"false)
28        if inst.components.container ~= nil then
29            inst.components.container:DropEverything()
30            inst.components.container:Close()
31        end
32    end
33end
34
35local function onbuilt(inst) --建造时的fn(可以直接照搬)
36    inst.AnimState:PlayAnimation("place_camppot"--播放建造动画
37    inst.AnimState:PushAnimation("idle"true--完成时播放idle
38    inst.SoundEmitter:PlaySound("dontstarve/common/cook_pot_craft")
39end
40
41local function onopen(inst) --开盖子的fn(可以直接照搬)
42    if not inst:HasTag("burnt"then
43        inst.AnimState:PlayAnimation("open",false)
44    local container = inst.components.container
45    for i = 1, container:GetNumSlots() do
46        local item = container:GetItemInSlot(i)
47        if item and item.components.perishable then
48            item.components.perishable.localPerishMultiplyer = 1 --腐烂速度乘数
49        end
50    end
51    inst.SoundEmitter:KillSound("snd")
52    inst.SoundEmitter:PlaySound("dontstarve/common/cookingpot_open")
53    inst.SoundEmitter:PlaySound("dontstarve/common/cookingpot""snd")
54    end
55end 
56
57local function onclose(inst) --关盖子的fn(照搬即可)
58    if not inst:HasTag("burnt"then
59        inst.AnimState:PlayAnimation("close",false)
60    local container = inst.components.container
61    for i = 1, container:GetNumSlots() do
62        local item = container:GetItemInSlot(i)
63        if item and item.components.perishable then
64            item.components.perishable.localPerishMultiplyer = 1
65        end
66    end
67    inst.SoundEmitter:KillSound("snd")
68    inst.SoundEmitter:PlaySound("dontstarve/common/cookingpot_close")
69    end
70end
71
72local function onsave(inst, data)
73    if inst:HasTag("burnt"or (inst.components.burnable ~= nil and inst.components.burnable:IsBurning()) then
74        data.burnt = true
75    end
76end
77
78local function onload(inst, data)
79    if data ~= nil and data.burnt then
80        inst.components.burnable.onburnt(inst)
81    end
82end
83
84local function fn()
85    local inst = CreateEntity()
86
87    inst.entity:AddTransform()
88    inst.entity:AddAnimState()
89    inst.entity:AddSoundEmitter()
90    --inst.entity:AddMiniMapEntity()
91    inst.entity:AddLight()
92    inst.entity:AddNetwork()
93
94    --inst.MiniMapEntity:SetIcon(name..".png")
95
96    inst:AddTag("structure")
97
98    inst.AnimState:SetBank("sollypot")
99    inst.AnimState:SetBuild("sollypot")
100    inst.AnimState:PlayAnimation("idle")
101
102    MakeSnowCoveredPristine(inst)
103
104    inst.entity:SetPristine()
105
106    inst.Light:Enable(false--添加光照,默认false
107    inst.Light:SetRadius(.6)
108    inst.Light:SetFalloff(1)
109    inst.Light:SetIntensity(.5)
110    inst.Light:SetColour(235/255,62/255,12/255)
111
112    if not TheWorld.ismastersim then
113        return inst
114    end
115
116    inst:AddComponent("inspectable")
117
118    inst:AddComponent("talker")
119
120    inst:AddComponent("lootdropper")
121    inst:AddComponent("workable")
122    inst.components.workable:SetWorkAction(ACTIONS.HAMMER)
123    inst.components.workable:SetWorkLeft(4)
124    inst.components.workable:SetOnFinishCallback(onhammered)
125    inst.components.workable:SetOnWorkCallback(onhit)
126
127    --MakeSmallBurnable(inst, nil, nil, true)
128    MakeMediumPropagator(inst)
129
130    inst:ListenForEvent("onbuilt", onbuilt)
131    MakeSnowCovered(inst)
132
133    --添加容器
134    inst:AddComponent("container")
135    inst.components.container.acceptsstacks = false --这里是容器内物品不可堆叠
136    local spconttab = {}
137    local containers = require("containers")
138    local old_widgetsetup = containers.widgetsetup
139    function containers.widgetsetup(container, prefab, data)
140        local t = data or spconttab[prefab or container.inst.prefab]
141        local pref = prefab or container.inst.prefab
142        if pref == "sollypot" then
143            local t = spconttab[pref]
144            if t ~= nil then
145                for k, v in pairs(t) do
146                    container[k] = v
147                end
148            container:SetNumSlots(container.widget.slotpos ~= nil and #container.widget.slotpos or 0)
149            end
150        else
151            return old_widgetsetup(container, prefab)
152        end
153    end
154    local pt = Vector3(inst.Transform:GetWorldPosition()) --我们获取一下锅子的位置,以便后续设置容器的相对位置,注意要用Vector3
155    spconttab.sollypot =
156    {
157        widget =
158        {
159            slotpos = {
160                Vector3(pt.x, pt.y-5+79, pt.z+30),
161                Vector3(pt.x, pt.y-5, pt.z+30),
162                Vector3(pt.x, pt.y-5-79, pt.z+30),
163            },
164            animbank = "ui_sollypot_1x3",
165            animbuild = "ui_sollypot_1x3",
166            pos = Vector3(pt.x+60, pt.y+150, pt.z+30), --这里是刚才获取到pt位置做一个偏移,以便容器显示在锅子的右上方
167            side_align_tip = 100,
168        },
169        type = "sollypot",
170    }
171
172    for k, v in pairs(spconttab) do
173        containers.MAXITEMSLOTS = math.max(containers.MAXITEMSLOTS, v.widget.slotpos ~= nil and #v.widget.slotpos or 0)
174    end
175    inst.components.container:WidgetSetup("sollypot")
176    inst.components.container.onopenfn = onopen
177    inst.components.container.onclosefn = onclose
178
179    inst.OnSave = onsave 
180    inst.OnLoad = onload
181
182    return inst
183end
184
185return Prefab("common/objects/sollypot", fn, assets),
186MakePlacer("common/sollypot_placer""sollypot""sollypot""idle")

以上一个简单的建筑类预制物就写好了(照搬即可)


下面我们要添加一个烹煮按钮,我们想要它能实现:能正确判断食谱,经过一段时间烹煮给出对应的产品

--添加烹煮按钮
local widgetbuttoninfo = {
 text = "boil", --按钮上的文字
 position = Vector3(pt.x, pt.y-5-79-59, pt.z+30), --按钮位置
 fn = function(inst)
 --这里是按钮功能部分[1]
 end }
inst.components.container.widget.buttoninfo = widgetbuttoninfo

--这里我们写了一个带有烹煮时间boiltime和产品productname参数的为按钮服务的功能函数boilbuttonpress()[2]

 1local function boilbuttonpress(inst,boiltime,productname)
2    inst.Light:Enable(true--烹煮按钮成功按下时会启用光照
3    inst.components.container:Close() --自动关闭容器
4    inst.AnimState:PlayAnimation("cooking_boil_small",true--播放烹煮动画
5    inst.components.container:DestroyContents() --删除容器内的食材
6    inst.components.container.canbeopened = false --使烹煮过程中容器无法打开
7    inst.SoundEmitter:KillSound("snd"--这里注意先kill声音snd,再播放声音snd,就不会出现可能出现的音效叠加问题
8    inst.SoundEmitter:PlaySound("dontstarve/common/cookingpot_rattle""snd")
9
10    inst.boiltimetask = inst:DoTaskInTime(boiltime, function() --这里用了一个DoTaskInTime,在烹煮时间到了后执行函数
11        inst.Light:Enable(false--烹煮完成 关闭光照
12        inst.components.container.canbeopened = true --容器可被正常打开
13        inst.AnimState:PlayAnimation("steam",false--播放完成动画
14        inst.AnimState:PushAnimation("idle"false)
15        inst.SoundEmitter:KillSound("snd")
16        inst.SoundEmitter:PlaySound("dontstarve/common/cookingpot_finish")
17        inst.components.container:GiveItem(SpawnPrefab(productname)) --给容器烹煮的产品
18        inst.boiltimetask = nil
19    end)
20end

下面我们看按钮功能部分[1],由于容器只有3个格子,我们直接去写个if判断一下容器内物品是不是对应的食材即可

1local icc = inst.components.container --这里一定要先local一下
2--例:食谱a 1个怪兽肉+1个大肉+1个大蒜 40秒= 怪味肉饼monstermeatloaf 
3if (icc:Has("monstermeat",1or icc:Has("monstermeat_cooked",1)) and (icc:Has("meat",1or icc:Has("meat_cooked",1)) and (icc:Has("garlic",1or icc:Has("garlic_cooked",1)) then
4    boilbuttonpress(inst,40,"monstermeatloaf")
5end
6--例:食谱b 2个便便+1个草 3秒= aoligei
7if icc:Has("poop",2and icc:Has("cutgrass",1then
8    boilbuttonpress(inst,3,"aoligei")
9end

制作完毕我发现了一个问题:
当烹煮过程中,我们退出了游戏再进时,锅子里的食材没了,同时因为烹煮没有完成所以得不到产品
解决这一问题的方法应该不少,我这里想的办法是:如果烹煮时退了游戏再进,直接返还3个原材料到容器内,一劳永逸:
先给boilbuttonpress()[2]里开头加几句:

local itm_a = inst.components.container.slots[1]
local itm_b = inst.components.container.slots[2]
local itm_c = inst.components.container.slots[3]
inst.desinv_a = itm_a.prefab
inst.desinv_b = itm_b.prefab
inst.desinv_c = itm_c.prefab

以上几句实现 先获取容器内物品再存起来
然后我们再写一下saveload:

 1local function onsave(inst, data)
2    if inst:HasTag("burnt"or (inst.components.burnable ~= nil and inst.components.burnable:IsBurning()) then
3        data.burnt = true
4    end
5    if inst.boiltimetask ~= nil then --当inst.boiltimetask不是无效值,也就是在烹煮过程中退出了游戏
6        data.boiltask = true
7        data.desinv_a = inst.desinv_a
8        data.desinv_b = inst.desinv_b
9        data.desinv_c = inst.desinv_c
10    else
11        data.boiltask = false
12    end
13end
14
15local function onload(inst, data)
16    if data ~= nil and data.burnt then
17        inst.components.burnable.onburnt(inst)
18    end
19    if data ~= nil and data.boiltask then
20        inst.Light:Enable(false)
21        inst.components.container:GiveItem(SpawnPrefab(data.desinv_a))
22        inst.components.container:GiveItem(SpawnPrefab(data.desinv_b))
23        inst.components.container:GiveItem(SpawnPrefab(data.desinv_c))
24    end
25end

问题解决

~以上