Creative: Make the placenode registration check for non-player placers
[oweals/minetest_game.git] / mods / farming / api.lua
1
2 -- Wear out hoes, place soil
3 -- TODO Ignore group:flower
4 farming.registered_plants = {}
5
6 farming.hoe_on_use = function(itemstack, user, pointed_thing, uses)
7         local pt = pointed_thing
8         -- check if pointing at a node
9         if not pt then
10                 return
11         end
12         if pt.type ~= "node" then
13                 return
14         end
15
16         local under = minetest.get_node(pt.under)
17         local p = {x=pt.under.x, y=pt.under.y+1, z=pt.under.z}
18         local above = minetest.get_node(p)
19
20         -- return if any of the nodes is not registered
21         if not minetest.registered_nodes[under.name] then
22                 return
23         end
24         if not minetest.registered_nodes[above.name] then
25                 return
26         end
27
28         -- check if the node above the pointed thing is air
29         if above.name ~= "air" then
30                 return
31         end
32
33         -- check if pointing at soil
34         if minetest.get_item_group(under.name, "soil") ~= 1 then
35                 return
36         end
37
38         -- check if (wet) soil defined
39         local regN = minetest.registered_nodes
40         if regN[under.name].soil == nil or regN[under.name].soil.wet == nil or regN[under.name].soil.dry == nil then
41                 return
42         end
43
44         if minetest.is_protected(pt.under, user:get_player_name()) then
45                 minetest.record_protection_violation(pt.under, user:get_player_name())
46                 return
47         end
48         if minetest.is_protected(pt.above, user:get_player_name()) then
49                 minetest.record_protection_violation(pt.above, user:get_player_name())
50                 return
51         end
52
53         -- turn the node into soil and play sound
54         minetest.set_node(pt.under, {name = regN[under.name].soil.dry})
55         minetest.sound_play("default_dig_crumbly", {
56                 pos = pt.under,
57                 gain = 0.5,
58         })
59
60         if not (creative and creative.is_enabled_for
61                         and creative.is_enabled_for(user:get_player_name())) then
62                 -- wear tool
63                 local wdef = itemstack:get_definition()
64                 itemstack:add_wear(65535/(uses-1))
65                 -- tool break sound
66                 if itemstack:get_count() == 0 and wdef.sound and wdef.sound.breaks then
67                         minetest.sound_play(wdef.sound.breaks, {pos = pt.above, gain = 0.5})
68                 end
69         end
70         return itemstack
71 end
72
73 -- Register new hoes
74 farming.register_hoe = function(name, def)
75         -- Check for : prefix (register new hoes in your mod's namespace)
76         if name:sub(1,1) ~= ":" then
77                 name = ":" .. name
78         end
79         -- Check def table
80         if def.description == nil then
81                 def.description = "Hoe"
82         end
83         if def.inventory_image == nil then
84                 def.inventory_image = "unknown_item.png"
85         end
86         if def.recipe == nil then
87                 def.recipe = {
88                         {"air","air",""},
89                         {"","group:stick",""},
90                         {"","group:stick",""}
91                 }
92         end
93         if def.max_uses == nil then
94                 def.max_uses = 30
95         end
96         -- Register the tool
97         minetest.register_tool(name, {
98                 description = def.description,
99                 inventory_image = def.inventory_image,
100                 on_use = function(itemstack, user, pointed_thing)
101                         return farming.hoe_on_use(itemstack, user, pointed_thing, def.max_uses)
102                 end,
103                 groups = def.groups,
104                 sound = {breaks = "default_tool_breaks"},
105         })
106         -- Register its recipe
107         if def.material == nil then
108                 minetest.register_craft({
109                         output = name:sub(2),
110                         recipe = def.recipe
111                 })
112         else
113                 minetest.register_craft({
114                         output = name:sub(2),
115                         recipe = {
116                                 {def.material, def.material, ""},
117                                 {"", "group:stick", ""},
118                                 {"", "group:stick", ""}
119                         }
120                 })
121         end
122 end
123
124 -- how often node timers for plants will tick, +/- some random value
125 local function tick(pos)
126         minetest.get_node_timer(pos):start(math.random(166, 286))
127 end
128 -- how often a growth failure tick is retried (e.g. too dark)
129 local function tick_again(pos)
130         minetest.get_node_timer(pos):start(math.random(40, 80))
131 end
132
133 -- Seed placement
134 farming.place_seed = function(itemstack, placer, pointed_thing, plantname)
135         local pt = pointed_thing
136         -- check if pointing at a node
137         if not pt then
138                 return itemstack
139         end
140         if pt.type ~= "node" then
141                 return itemstack
142         end
143
144         local under = minetest.get_node(pt.under)
145         local above = minetest.get_node(pt.above)
146
147         if minetest.is_protected(pt.under, placer:get_player_name()) then
148                 minetest.record_protection_violation(pt.under, placer:get_player_name())
149                 return
150         end
151         if minetest.is_protected(pt.above, placer:get_player_name()) then
152                 minetest.record_protection_violation(pt.above, placer:get_player_name())
153                 return
154         end
155
156         -- return if any of the nodes is not registered
157         if not minetest.registered_nodes[under.name] then
158                 return itemstack
159         end
160         if not minetest.registered_nodes[above.name] then
161                 return itemstack
162         end
163
164         -- check if pointing at the top of the node
165         if pt.above.y ~= pt.under.y+1 then
166                 return itemstack
167         end
168
169         -- check if you can replace the node above the pointed node
170         if not minetest.registered_nodes[above.name].buildable_to then
171                 return itemstack
172         end
173
174         -- check if pointing at soil
175         if minetest.get_item_group(under.name, "soil") < 2 then
176                 return itemstack
177         end
178
179         -- add the node and remove 1 item from the itemstack
180         minetest.add_node(pt.above, {name = plantname, param2 = 1})
181         tick(pt.above)
182         if not (creative and creative.is_enabled_for
183                         and creative.is_enabled_for(placer:get_player_name())) then
184                 itemstack:take_item()
185         end
186         return itemstack
187 end
188
189 farming.grow_plant = function(pos, elapsed)
190         local node = minetest.get_node(pos)
191         local name = node.name
192         local def = minetest.registered_nodes[name]
193
194         if not def.next_plant then
195                 -- disable timer for fully grown plant
196                 return
197         end
198
199         -- grow seed
200         if minetest.get_item_group(node.name, "seed") and def.fertility then
201                 local soil_node = minetest.get_node_or_nil({x = pos.x, y = pos.y - 1, z = pos.z})
202                 if not soil_node then
203                         tick_again(pos)
204                         return
205                 end
206                 -- omitted is a check for light, we assume seeds can germinate in the dark.
207                 for _, v in pairs(def.fertility) do
208                         if minetest.get_item_group(soil_node.name, v) ~= 0 then
209                                 local placenode = {name = def.next_plant}
210                                 if def.place_param2 then
211                                         placenode.param2 = def.place_param2
212                                 end
213                                 minetest.swap_node(pos, placenode)
214                                 if minetest.registered_nodes[def.next_plant].next_plant then
215                                         tick(pos)
216                                         return
217                                 end
218                         end
219                 end
220
221                 return
222         end
223
224         -- check if on wet soil
225         local below = minetest.get_node({x = pos.x, y = pos.y - 1, z = pos.z})
226         if minetest.get_item_group(below.name, "soil") < 3 then
227                 tick_again(pos)
228                 return
229         end
230
231         -- check light
232         local light = minetest.get_node_light(pos)
233         if not light or light < def.minlight or light > def.maxlight then
234                 tick_again(pos)
235                 return
236         end
237
238         -- grow
239         local placenode = {name = def.next_plant}
240         if def.place_param2 then
241                 placenode.param2 = def.place_param2
242         end
243         minetest.swap_node(pos, placenode)
244
245         -- new timer needed?
246         if minetest.registered_nodes[def.next_plant].next_plant then
247                 tick(pos)
248         end
249         return
250 end
251
252 -- Register plants
253 farming.register_plant = function(name, def)
254         local mname = name:split(":")[1]
255         local pname = name:split(":")[2]
256
257         -- Check def table
258         if not def.description then
259                 def.description = "Seed"
260         end
261         if not def.inventory_image then
262                 def.inventory_image = "unknown_item.png"
263         end
264         if not def.steps then
265                 return nil
266         end
267         if not def.minlight then
268                 def.minlight = 1
269         end
270         if not def.maxlight then
271                 def.maxlight = 14
272         end
273         if not def.fertility then
274                 def.fertility = {}
275         end
276
277         farming.registered_plants[pname] = def
278
279         -- Register seed
280         local lbm_nodes = {mname .. ":seed_" .. pname}
281         local g = {seed = 1, snappy = 3, attached_node = 1, flammable = 2}
282         for k, v in pairs(def.fertility) do
283                 g[v] = 1
284         end
285         minetest.register_node(":" .. mname .. ":seed_" .. pname, {
286                 description = def.description,
287                 tiles = {def.inventory_image},
288                 inventory_image = def.inventory_image,
289                 wield_image = def.inventory_image,
290                 drawtype = "signlike",
291                 groups = g,
292                 paramtype = "light",
293                 paramtype2 = "wallmounted",
294                 place_param2 = def.place_param2 or nil, -- this isn't actually used for placement
295                 walkable = false,
296                 sunlight_propagates = true,
297                 selection_box = {
298                         type = "fixed",
299                         fixed = {-0.5, -0.5, -0.5, 0.5, -5/16, 0.5},
300                 },
301                 fertility = def.fertility,
302                 sounds = default.node_sound_dirt_defaults({
303                         dig = {name = "", gain = 0},
304                         dug = {name = "default_grass_footstep", gain = 0.2},
305                         place = {name = "default_place_node", gain = 0.25},
306                 }),
307
308                 on_place = function(itemstack, placer, pointed_thing)
309                         local under = pointed_thing.under
310                         local node = minetest.get_node(under)
311                         local udef = minetest.registered_nodes[node.name]
312                         if udef and udef.on_rightclick and
313                                         not (placer and placer:get_player_control().sneak) then
314                                 return udef.on_rightclick(under, node, placer, itemstack,
315                                         pointed_thing) or itemstack
316                         end
317
318                         return farming.place_seed(itemstack, placer, pointed_thing, mname .. ":seed_" .. pname)
319                 end,
320                 next_plant = mname .. ":" .. pname .. "_1",
321                 on_timer = farming.grow_plant,
322                 minlight = def.minlight,
323                 maxlight = def.maxlight,
324         })
325
326         -- Register harvest
327         minetest.register_craftitem(":" .. mname .. ":" .. pname, {
328                 description = pname:gsub("^%l", string.upper),
329                 inventory_image = mname .. "_" .. pname .. ".png",
330                 groups = {flammable = 2},
331         })
332
333         -- Register growing steps
334         for i = 1, def.steps do
335                 local base_rarity = 1
336                 if def.steps ~= 1 then
337                         base_rarity =  8 - (i - 1) * 7 / (def.steps - 1)
338                 end
339                 local drop = {
340                         items = {
341                                 {items = {mname .. ":" .. pname}, rarity = base_rarity},
342                                 {items = {mname .. ":" .. pname}, rarity = base_rarity * 2},
343                                 {items = {mname .. ":seed_" .. pname}, rarity = base_rarity},
344                                 {items = {mname .. ":seed_" .. pname}, rarity = base_rarity * 2},
345                         }
346                 }
347                 local nodegroups = {snappy = 3, flammable = 2, plant = 1, not_in_creative_inventory = 1, attached_node = 1}
348                 nodegroups[pname] = i
349
350                 local next_plant = nil
351
352                 if i < def.steps then
353                         next_plant = mname .. ":" .. pname .. "_" .. (i + 1)
354                         lbm_nodes[#lbm_nodes + 1] = mname .. ":" .. pname .. "_" .. i
355                 end
356
357                 minetest.register_node(":" .. mname .. ":" .. pname .. "_" .. i, {
358                         drawtype = "plantlike",
359                         waving = 1,
360                         tiles = {mname .. "_" .. pname .. "_" .. i .. ".png"},
361                         paramtype = "light",
362                         paramtype2 = def.paramtype2 or nil,
363                         place_param2 = def.place_param2 or nil,
364                         walkable = false,
365                         buildable_to = true,
366                         drop = drop,
367                         selection_box = {
368                                 type = "fixed",
369                                 fixed = {-0.5, -0.5, -0.5, 0.5, -5/16, 0.5},
370                         },
371                         groups = nodegroups,
372                         sounds = default.node_sound_leaves_defaults(),
373                         next_plant = next_plant,
374                         on_timer = farming.grow_plant,
375                         minlight = def.minlight,
376                         maxlight = def.maxlight,
377                 })
378         end
379
380         -- replacement LBM for pre-nodetimer plants
381         minetest.register_lbm({
382                 name = ":" .. mname .. ":start_nodetimer_" .. pname,
383                 nodenames = lbm_nodes,
384                 action = function(pos, node)
385                         tick_again(pos)
386                 end,
387         })
388
389         -- Return
390         local r = {
391                 seed = mname .. ":seed_" .. pname,
392                 harvest = mname .. ":" .. pname
393         }
394         return r
395 end