2940 lines
96 KiB
Lua
2940 lines
96 KiB
Lua
--[[
|
|
X Farming. Extends Minetest farming mod with new plants, crops and ice fishing.
|
|
Copyright (C) 2024 SaKeL
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to juraj.vajda@gmail.com
|
|
--]]
|
|
|
|
local S = minetest.get_translator(minetest.get_current_modname())
|
|
|
|
-- main class
|
|
x_farming = {
|
|
hunger_ng = minetest.get_modpath('hunger_ng'),
|
|
hbhunger = minetest.get_modpath('hbhunger'),
|
|
vessels = minetest.get_modpath('vessels'),
|
|
bucket = minetest.get_modpath('bucket'),
|
|
colors = {
|
|
brown = '#DEB887',
|
|
red = '#FF8080',
|
|
green = '#98E698'
|
|
},
|
|
x_bonemeal = {
|
|
tree_defs = {
|
|
['x_farming:christmas_tree_sapling'] = {
|
|
-- christmas tree
|
|
name = 'x_farming:christmas_tree_sapling',
|
|
chance = 2,
|
|
grow_tree = function(pos)
|
|
if not x_farming.x_bonemeal.is_on_soil(pos) then
|
|
return false
|
|
end
|
|
|
|
x_farming.grow_christmas_tree(pos)
|
|
|
|
return true
|
|
end
|
|
},
|
|
['x_farming:kiwi_sapling'] = {
|
|
-- Kiwi Tree
|
|
name = 'x_farming:kiwi_sapling',
|
|
chance = 2,
|
|
grow_tree = function(pos)
|
|
if not x_farming.x_bonemeal.is_on_soil(pos) then
|
|
return false
|
|
end
|
|
|
|
x_farming.grow_kiwi_tree(pos)
|
|
|
|
return true
|
|
end
|
|
},
|
|
['x_farming:large_cactus_with_fruit_seedling'] = {
|
|
-- Cactus Seedling
|
|
name = 'x_farming:large_cactus_with_fruit_seedling',
|
|
chance = 2,
|
|
grow_tree = function(pos)
|
|
if not x_farming.x_bonemeal.is_on_sand(pos) then
|
|
return false
|
|
end
|
|
|
|
x_farming.grow_large_cactus(pos)
|
|
|
|
return true
|
|
end
|
|
},
|
|
['x_farming:jungle_with_cocoa_sapling'] = {
|
|
-- Jungle Tree with Cocoa
|
|
name = 'x_farming:jungle_with_cocoa_sapling',
|
|
chance = 2,
|
|
grow_tree = function(pos)
|
|
if not x_farming.x_bonemeal.is_on_soil(pos) then
|
|
return false
|
|
end
|
|
|
|
x_farming.grow_jungle_tree(pos)
|
|
|
|
return true
|
|
end
|
|
},
|
|
['x_farming:pine_nut_sapling'] = {
|
|
-- Pine Nut Tree
|
|
name = 'x_farming:pine_nut_sapling',
|
|
chance = 2,
|
|
grow_tree = function(pos)
|
|
if not x_farming.x_bonemeal.is_on_soil(pos) then
|
|
return false
|
|
end
|
|
|
|
x_farming.grow_pine_nut_tree(pos)
|
|
|
|
return true
|
|
end
|
|
},
|
|
}
|
|
},
|
|
allowed_crate_items = {},
|
|
allowed_bag_items = {},
|
|
registered_crates = {},
|
|
lbm_nodenames_crates = {},
|
|
registered_plants = {},
|
|
mcl = {},
|
|
candle_colors = {
|
|
black = {
|
|
name = S('Black'),
|
|
hex = '#2B2B2B',
|
|
mcl_groups = { basecolor_black = 1, excolor_black = 1, unicolor_black = 1 }
|
|
},
|
|
dark_grey = {
|
|
name = S('Dark Grey'),
|
|
hex = '#4E4E4E',
|
|
mcl_groups = { basecolor_grey = 1, excolor_darkgrey = 1, unicolor_darkgrey = 1 }
|
|
},
|
|
grey = {
|
|
name = S('Grey'),
|
|
hex = '#A5A5A5',
|
|
mcl_groups = { basecolor_grey = 1, excolor_grey = 1, unicolor_grey = 1 }
|
|
},
|
|
red = {
|
|
name = S('Red'),
|
|
hex = '#AB5C4A',
|
|
mcl_groups = { basecolor_red = 1, excolor_red = 1, unicolor_red = 1 }
|
|
},
|
|
violet = {
|
|
name = S('Violet'),
|
|
hex = '#595287',
|
|
mcl_groups = { basecolor_magenta = 1, excolor_violet = 1, unicolor_violet = 1 }
|
|
},
|
|
magenta = {
|
|
name = S('Magenta'),
|
|
hex = '#A25B5D',
|
|
mcl_groups = { basecolor_magenta = 1, excolor_red_violet = 1, unicolor_red_violet = 1 }
|
|
},
|
|
pink = {
|
|
name = S('Pink'),
|
|
hex = '#FFA6A6',
|
|
mcl_groups = { basecolor_red = 1, excolor_red = 1, unicolor_light_red = 1 }
|
|
},
|
|
dark_green = {
|
|
name = S('Dark Green'),
|
|
hex = '#556E48',
|
|
mcl_groups = { basecolor_green = 1, excolor_green = 1, unicolor_dark_green = 1 }
|
|
},
|
|
green = {
|
|
name = S('Green'),
|
|
hex = '#779154',
|
|
mcl_groups = { basecolor_green = 1, excolor_green = 1, unicolor_green = 1 }
|
|
},
|
|
cyan = {
|
|
name = S('Cyan'),
|
|
hex = '#4E7683',
|
|
mcl_groups = { basecolor_cyan = 1, excolor_cyan = 1, unicolor_cyan = 1 }
|
|
},
|
|
blue = {
|
|
name = S('Blue'),
|
|
hex = '#4B6696',
|
|
mcl_groups = { basecolor_blue = 1, excolor_blue = 1, unicolor_blue = 1 }
|
|
},
|
|
light_blue = {
|
|
name = S('Light Blue'),
|
|
hex = '#648CB4',
|
|
craft_dye = 'group:dye,color_light_blue',
|
|
mcl_groups = { basecolor_blue = 1, excolor_blue = 1, unicolor_light_blue = 1 }
|
|
},
|
|
orange = {
|
|
name = S('Orange'),
|
|
hex = '#A86A4D',
|
|
mcl_groups = { basecolor_orange = 1, excolor_orange = 1, unicolor_orange = 1 }
|
|
},
|
|
yellow = {
|
|
name = S('Yellow'),
|
|
hex = '#BD8D39',
|
|
mcl_groups = { basecolor_yellow = 1, excolor_yellow = 1, unicolor_yellow = 1 }
|
|
},
|
|
brown = {
|
|
name = S('Brown'),
|
|
hex = '#684E45',
|
|
mcl_groups = { basecolor_brown = 1, excolor_orange = 1, unicolor_dark_orange = 1 }
|
|
}
|
|
},
|
|
}
|
|
|
|
function x_farming.node_sound_grass_defaults(table)
|
|
table = table or {}
|
|
table.footstep = table.footstep or { name = 'x_farming_grass_footstep', gain = 0.4 }
|
|
table.dig = table.dig or { name = 'x_farming_grass_hit', gain = 1.2 }
|
|
table.dug = table.dug or { name = 'x_farming_dirt_hit', gain = 1.0 }
|
|
table.place = table.place or { name = 'x_farming_dirt_hit', gain = 1.0 }
|
|
return table
|
|
end
|
|
|
|
function x_farming.node_sound_leaves_defaults(table)
|
|
table = table or {}
|
|
table.footstep = table.footstep or { name = 'x_farming_leaves_footstep', gain = 0.1 }
|
|
table.dig = table.dig or { name = 'x_farming_leaves_hit', gain = 0.25 }
|
|
table.dug = table.dug or { name = 'x_farming_leaves_dug', gain = 0.5 }
|
|
table.place = table.place or { name = 'x_farming_leaves_place', gain = 0.4 }
|
|
return table
|
|
end
|
|
|
|
function x_farming.node_sound_wood_defaults(table)
|
|
table = table or {}
|
|
table.footstep = table.footstep or { name = 'x_farming_wood_footstep', gain = 0.15 }
|
|
table.dig = table.dig or { name = 'x_farming_wood_hit', gain = 0.5 }
|
|
table.dug = table.dug or { name = 'x_farming_wood_place', gain = 0.1 }
|
|
table.place = table.place or { name = 'x_farming_wood_place', gain = 0.15 }
|
|
return table
|
|
end
|
|
|
|
function x_farming.node_sound_sand_defaults(table)
|
|
table = table or {}
|
|
table.footstep = table.footstep or { name = 'x_farming_sand_footstep', gain = 0.1 }
|
|
table.dig = table.dig or { name = 'x_farming_sand_hit', gain = 0.5 }
|
|
table.dug = table.dug or { name = 'x_farming_sand_dug', gain = 0.1 }
|
|
table.place = table.place or { name = 'x_farming_sand_place', gain = 0.15 }
|
|
return table
|
|
end
|
|
|
|
function x_farming.node_sound_thin_glass_defaults(table)
|
|
table = table or {}
|
|
table.footstep = table.footstep or { name = 'x_farming_thin_glass_footstep', gain = 0.3 }
|
|
table.dig = table.dig or { name = 'x_farming_thin_glass_footstep', gain = 0.5 }
|
|
table.dug = table.dug or { name = 'x_farming_break_thin_glass', gain = 1.0 }
|
|
table.place = table.place or { name = 'x_farming_glass_place', gain = 0.2 }
|
|
return table
|
|
end
|
|
|
|
function x_farming.node_sound_dirt_defaults(table)
|
|
table = table or {}
|
|
table.footstep = table.footstep or { name = 'x_farming_dirt_footstep', gain = 0.15 }
|
|
table.dig = table.dig or { name = 'x_farming_dirt_hit', gain = 0.4 }
|
|
table.dug = table.dug or { name = 'x_farming_dirt_hit', gain = 1.0 }
|
|
table.place = table.place or { name = 'x_farming_dirt_hit', gain = 1.0 }
|
|
return table
|
|
end
|
|
|
|
function x_farming.node_sound_ice_defaults(table)
|
|
table = table or {}
|
|
table.footstep = table.footstep or { name = 'x_farming_ice_footstep', gain = 0.2 }
|
|
table.dig = table.dig or { name = 'x_farming_ice_hit', gain = 0.4 }
|
|
table.dug = table.dug or { name = 'x_farming_ice_hit', gain = 1.0 }
|
|
table.place = table.place or { name = 'x_farming_ice_hit', gain = 1.0 }
|
|
return table
|
|
end
|
|
|
|
function x_farming.node_sound_stone_defaults(table)
|
|
table = table or {}
|
|
table.footstep = table.footstep or { name = 'x_farming_stone_footstep', gain = 0.2 }
|
|
table.dig = table.dig or { name = 'x_farming_stone_hit', gain = 1.0 }
|
|
table.dug = table.dug or { name = 'x_farming_stone_dug', gain = 0.6 }
|
|
table.place = table.place or { name = 'x_farming_stone_place', gain = 1.0 }
|
|
return table
|
|
end
|
|
|
|
function x_farming.node_sound_slime_defaults(table)
|
|
table = table or {}
|
|
table.footstep = table.footstep or { name = 'x_farming_slime_footstep', gain = 0.2 }
|
|
table.dig = table.dig or { name = 'x_farming_slime_dig', gain = 1.0 }
|
|
table.dug = table.dug or { name = 'x_farming_slime_dug', gain = 0.3 }
|
|
table.place = table.place or { name = 'x_farming_slime_footstep', gain = 1.0 }
|
|
return table
|
|
end
|
|
|
|
function x_farming.node_sound_rope_defaults(table)
|
|
table = table or {}
|
|
table.footstep = table.footstep or { name = 'x_farming_rope_footstep', gain = 0.05 }
|
|
table.dig = table.dig or { name = 'x_farming_rope_hit', gain = 0.7 }
|
|
table.dug = table.dug or { name = 'x_farming_rope_dug', gain = 0.2 }
|
|
table.place = table.place or { name = 'x_farming_rope_hit', gain = 0.8 }
|
|
return table
|
|
end
|
|
|
|
function x_farming.node_sound_pillow_defaults(table)
|
|
table = table or {}
|
|
table.footstep = table.footstep or { name = 'x_farming_pillow_footstep', gain = 0.1 }
|
|
table.dig = table.dig or { name = 'x_farming_pillow_hit', gain = 0.15 }
|
|
table.dug = table.dug or { name = 'x_farming_pillow_dug', gain = 0.2 }
|
|
table.place = table.place or { name = 'x_farming_pillow_footstep', gain = 0.25 }
|
|
return table
|
|
end
|
|
|
|
---how often node timers for plants will tick, +/- some random value
|
|
function x_farming.tick_block(pos)
|
|
minetest.get_node_timer(pos):start(math.random(498, 1287))
|
|
end
|
|
|
|
---how often a growth failure tick is retried (e.g. too dark)
|
|
function x_farming.tick_block_short(pos)
|
|
minetest.get_node_timer(pos):start(math.random(332, 858))
|
|
end
|
|
|
|
-- how often node timers for plants will tick, +/- some random value
|
|
function x_farming.tick(pos)
|
|
minetest.get_node_timer(pos):start(math.random(166, 286))
|
|
end
|
|
-- how often a growth failure tick is retried (e.g. too dark)
|
|
function x_farming.tick_again(pos)
|
|
minetest.get_node_timer(pos):start(math.random(40, 80))
|
|
end
|
|
|
|
---just shorthand for minetest metadata handling
|
|
function x_farming.meta_get_str(key, pos)
|
|
local meta = minetest.get_meta(pos)
|
|
return meta:get_string(key)
|
|
end
|
|
|
|
---just shorthand for minetest metadata handling
|
|
function x_farming.meta_set_str(key, value, pos)
|
|
local meta = minetest.get_meta(pos)
|
|
meta:set_string(key, value)
|
|
end
|
|
|
|
---merge two indexed tables
|
|
function x_farming.mergeTables(t1, t2)
|
|
local _t1 = { unpack(t1) }
|
|
local _t2 = { unpack(t2) }
|
|
|
|
for k, v in ipairs(_t2) do
|
|
table.insert(_t1, v)
|
|
end
|
|
|
|
return _t1
|
|
end
|
|
|
|
---Change an entire string to Title Case (i.e. capitalise the first letter of each word)
|
|
function x_farming.tchelper(first, rest)
|
|
return first:upper() .. rest:lower()
|
|
end
|
|
|
|
--- Hoes - copy from MTG
|
|
|
|
x_farming.hoe_on_use = function(itemstack, user, pointed_thing, uses)
|
|
local pt = pointed_thing
|
|
-- check if pointing at a node
|
|
if not pt then
|
|
return
|
|
end
|
|
if pt.type ~= 'node' then
|
|
return
|
|
end
|
|
|
|
local under = minetest.get_node(pt.under)
|
|
local p = { x = pt.under.x, y = pt.under.y + 1, z = pt.under.z }
|
|
local above = minetest.get_node(p)
|
|
|
|
-- return if any of the nodes is not registered
|
|
if not minetest.registered_nodes[under.name] then
|
|
return
|
|
end
|
|
if not minetest.registered_nodes[above.name] then
|
|
return
|
|
end
|
|
|
|
-- check if the node above the pointed thing is air
|
|
if above.name ~= 'air' then
|
|
return
|
|
end
|
|
|
|
-- check if pointing at soil
|
|
if minetest.get_item_group(under.name, 'soil') ~= 1 then
|
|
return
|
|
end
|
|
|
|
-- check if (wet) soil defined
|
|
local regN = minetest.registered_nodes
|
|
if regN[under.name].soil == nil or regN[under.name].soil.wet == nil or regN[under.name].soil.dry == nil then
|
|
return
|
|
end
|
|
|
|
local player_name = user and user:get_player_name() or ''
|
|
|
|
if minetest.is_protected(pt.under, player_name) then
|
|
minetest.record_protection_violation(pt.under, player_name)
|
|
return
|
|
end
|
|
|
|
if minetest.is_protected(pt.above, player_name) then
|
|
minetest.record_protection_violation(pt.above, player_name)
|
|
return
|
|
end
|
|
|
|
-- turn the node into soil and play sound
|
|
minetest.set_node(pt.under, { name = regN[under.name].soil.dry })
|
|
minetest.sound_play('x_farming_dirt_hit', {
|
|
pos = pt.under,
|
|
gain = 0.3,
|
|
}, true)
|
|
|
|
if not minetest.is_creative_enabled(player_name) then
|
|
-- wear tool
|
|
local wdef = itemstack:get_definition()
|
|
itemstack:add_wear_by_uses(uses)
|
|
-- tool break sound
|
|
if itemstack:get_count() == 0 and wdef.sound and wdef.sound.breaks then
|
|
minetest.sound_play(wdef.sound.breaks, {pos = pt.above,
|
|
gain = 0.5}, true)
|
|
end
|
|
end
|
|
return itemstack
|
|
end
|
|
|
|
-- Register new hoes
|
|
x_farming.register_hoe = function(name, def)
|
|
local _def = table.copy(def)
|
|
-- Check for : prefix (register new hoes in your mod's namespace)
|
|
if name:sub(1, 1) ~= ':' then
|
|
name = ':' .. name
|
|
end
|
|
-- Check def table
|
|
if _def.description == nil then
|
|
_def.description = S('Hoe')
|
|
end
|
|
if _def.inventory_image == nil then
|
|
_def.inventory_image = 'x_farming_unknown_item.png'
|
|
end
|
|
if _def.max_uses == nil then
|
|
_def.max_uses = 30
|
|
end
|
|
|
|
if minetest.get_modpath('farming') then
|
|
_def.on_use = function(itemstack, user, pointed_thing)
|
|
return x_farming.hoe_on_use(itemstack, user, pointed_thing, _def.max_uses)
|
|
end
|
|
end
|
|
|
|
if minetest.get_modpath('mcl_farming') then
|
|
_def.on_place = x_farming.mcl.hoe_on_place_function(_def.max_uses)
|
|
end
|
|
|
|
-- MCL
|
|
_def.groups = _def.groups
|
|
_def.sound = { breaks = 'x_farming_tool_breaks' }
|
|
_def.wield_scale = _def.wield_scale or { x = 1, y = 1, z = 1 }
|
|
|
|
-- Register the tool
|
|
minetest.register_tool(name, _def)
|
|
-- Register its recipe
|
|
if _def.recipe then
|
|
minetest.register_craft({
|
|
output = name:sub(2),
|
|
recipe = _def.recipe
|
|
})
|
|
elseif _def.material then
|
|
minetest.register_craft({
|
|
output = name:sub(2),
|
|
recipe = {
|
|
{ _def.material, 'group:stick' },
|
|
{ _def.material, 'group:stick' },
|
|
{ '', 'group:stick' }
|
|
}
|
|
})
|
|
end
|
|
end
|
|
|
|
--
|
|
-- Log API / helpers - copy from MTG
|
|
--
|
|
|
|
local log_non_player_actions = minetest.settings:get_bool('log_non_player_actions', false)
|
|
|
|
local is_pos = function(v)
|
|
return type(v) == 'table' and
|
|
type(v.x) == 'number' and type(v.y) == 'number' and type(v.z) == 'number'
|
|
end
|
|
|
|
function x_farming.log_player_action(player, ...)
|
|
local msg = player:get_player_name()
|
|
if player.is_fake_player or not player:is_player() then
|
|
if not log_non_player_actions then
|
|
return
|
|
end
|
|
msg = msg .. '(' .. (type(player.is_fake_player) == 'string'
|
|
and player.is_fake_player or '*') .. ')'
|
|
end
|
|
for _, v in ipairs({ ... }) do
|
|
-- translate pos
|
|
local part = is_pos(v) and minetest.pos_to_string(v) or v
|
|
-- no leading spaces before punctuation marks
|
|
msg = msg .. (string.match(part, '^[;,.]') and '' or ' ') .. part
|
|
end
|
|
minetest.log('action', msg)
|
|
end
|
|
|
|
function x_farming.set_inventory_action_loggers(def, name)
|
|
def.on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
|
|
x_farming.log_player_action(player, 'moves stuff in', name, 'at', pos)
|
|
end
|
|
def.on_metadata_inventory_put = function(pos, listname, index, stack, player)
|
|
x_farming.log_player_action(player, 'moves', stack:get_name(), 'to', name, 'at', pos)
|
|
end
|
|
def.on_metadata_inventory_take = function(pos, listname, index, stack, player)
|
|
x_farming.log_player_action(player, 'takes', stack:get_name(), 'from', name, 'at', pos)
|
|
end
|
|
end
|
|
|
|
--
|
|
-- Sapling 'on place' function to check protection of node and resulting tree volume
|
|
--
|
|
|
|
function x_farming.sapling_on_place(itemstack, placer, pointed_thing, sapling_name, minp_relative, maxp_relative, interval)
|
|
-- Position of sapling
|
|
local pos = pointed_thing.under
|
|
local node = minetest.get_node_or_nil(pos)
|
|
local pdef = node and minetest.registered_nodes[node.name]
|
|
|
|
if not node then
|
|
return itemstack
|
|
end
|
|
|
|
if pdef and pdef.on_rightclick and
|
|
not (placer and placer:is_player() and
|
|
placer:get_player_control().sneak)
|
|
then
|
|
return pdef.on_rightclick(pos, node, placer, itemstack, pointed_thing)
|
|
end
|
|
|
|
if not pdef or not pdef.buildable_to then
|
|
pos = pointed_thing.above
|
|
node = minetest.get_node_or_nil(pos)
|
|
pdef = node and minetest.registered_nodes[node.name]
|
|
if not pdef or not pdef.buildable_to then
|
|
return itemstack
|
|
end
|
|
end
|
|
|
|
local player_name = placer and placer:get_player_name() or ''
|
|
|
|
-- Check sapling position for protection
|
|
if minetest.is_protected(pos, player_name) then
|
|
minetest.record_protection_violation(pos, player_name)
|
|
return itemstack
|
|
end
|
|
|
|
-- Check tree volume for protection
|
|
if minetest.is_area_protected(
|
|
vector.add(pos, minp_relative),
|
|
vector.add(pos, maxp_relative),
|
|
player_name,
|
|
interval) then
|
|
minetest.record_protection_violation(pos, player_name)
|
|
-- Print extra information to explain
|
|
-- minetest.chat_send_player(player_name,
|
|
-- itemstack:get_definition().description .. ' will intersect protection ' ..
|
|
-- 'on growth')
|
|
minetest.chat_send_player(
|
|
player_name,
|
|
S('@1 will intersect protection on growth.', itemstack:get_definition().description)
|
|
)
|
|
return itemstack
|
|
end
|
|
|
|
x_farming.log_player_action(placer, 'places node', sapling_name, 'at', pos)
|
|
|
|
local take_item = not minetest.is_creative_enabled(player_name)
|
|
local newnode = { name = sapling_name }
|
|
local ndef = minetest.registered_nodes[sapling_name]
|
|
minetest.set_node(pos, newnode)
|
|
|
|
-- Run callback
|
|
if ndef and ndef.after_place_node then
|
|
-- Deepcopy place_to and pointed_thing because callback can modify it
|
|
if ndef.after_place_node(table.copy(pos), placer, itemstack, table.copy(pointed_thing)) then
|
|
take_item = false
|
|
end
|
|
end
|
|
|
|
-- Run script hook
|
|
for _, callback in ipairs(minetest.registered_on_placenodes) do
|
|
-- Deepcopy pos, node and pointed_thing because callback can modify them
|
|
if callback(table.copy(pos), table.copy(newnode),
|
|
placer, table.copy(node or {}),
|
|
itemstack, table.copy(pointed_thing)) then
|
|
take_item = false
|
|
end
|
|
end
|
|
|
|
if take_item then
|
|
itemstack:take_item()
|
|
end
|
|
|
|
return itemstack
|
|
end
|
|
|
|
--
|
|
-- Leafdecay
|
|
--
|
|
|
|
-- Prevent decay of placed leaves
|
|
|
|
function x_farming.after_place_leaves(pos, placer, itemstack, pointed_thing)
|
|
if placer and placer:is_player() then
|
|
local node = minetest.get_node(pos)
|
|
node.param2 = 1
|
|
minetest.set_node(pos, node)
|
|
end
|
|
end
|
|
|
|
-- Leafdecay
|
|
local function leafdecay_after_destruct(pos, oldnode, def)
|
|
for _, v in pairs(minetest.find_nodes_in_area(vector.subtract(pos, def.radius), vector.add(pos, def.radius), def.leaves)) do
|
|
local node = minetest.get_node(v)
|
|
local timer = minetest.get_node_timer(v)
|
|
if (node.param2 ~= 1 or minetest.get_item_group('cocoa') == 0) and not timer:is_started() then
|
|
timer:start(math.random(20, 120) / 10)
|
|
end
|
|
end
|
|
end
|
|
|
|
local movement_gravity = tonumber(minetest.settings:get('movement_gravity')) or 9.81
|
|
|
|
local function leafdecay_on_timer(pos, def)
|
|
if minetest.find_node_near(pos, def.radius, def.trunks) then
|
|
return false
|
|
end
|
|
|
|
local node = minetest.get_node(pos)
|
|
local drops = minetest.get_node_drops(node.name)
|
|
for _, item in ipairs(drops) do
|
|
local is_leaf
|
|
for _, v in pairs(def.leaves) do
|
|
if v == item then
|
|
is_leaf = true
|
|
end
|
|
end
|
|
|
|
if minetest.get_item_group(item, 'leafdecay_drop') ~= 0 or not is_leaf then
|
|
minetest.add_item({
|
|
x = pos.x - 0.5 + math.random(),
|
|
y = pos.y - 0.5 + math.random(),
|
|
z = pos.z - 0.5 + math.random(),
|
|
}, item)
|
|
end
|
|
end
|
|
|
|
minetest.remove_node(pos)
|
|
minetest.check_for_falling(pos)
|
|
|
|
-- spawn a few particles for the removed node
|
|
minetest.add_particlespawner({
|
|
amount = 8,
|
|
time = 0.001,
|
|
minpos = vector.subtract(pos, { x = 0.5, y = 0.5, z = 0.5 }),
|
|
maxpos = vector.add(pos, { x = 0.5, y = 0.5, z = 0.5 }),
|
|
minvel = vector.new(-0.5, -1, -0.5),
|
|
maxvel = vector.new(0.5, 0, 0.5),
|
|
minacc = vector.new(0, -movement_gravity, 0),
|
|
maxacc = vector.new(0, -movement_gravity, 0),
|
|
minsize = 0,
|
|
maxsize = 0,
|
|
node = node,
|
|
})
|
|
end
|
|
|
|
function x_farming.register_leafdecay(def)
|
|
assert(def.leaves)
|
|
assert(def.trunks)
|
|
assert(def.radius)
|
|
|
|
for _, v in pairs(def.trunks) do
|
|
minetest.override_item(v, {
|
|
after_destruct = function(pos, oldnode)
|
|
leafdecay_after_destruct(pos, oldnode, def)
|
|
end,
|
|
})
|
|
end
|
|
|
|
for _, v in pairs(def.leaves) do
|
|
minetest.override_item(v, {
|
|
on_timer = function(pos)
|
|
leafdecay_on_timer(pos, def)
|
|
end,
|
|
})
|
|
end
|
|
end
|
|
|
|
-- Seed placement - copy from MTG
|
|
function x_farming.place_seed(itemstack, placer, pointed_thing, plantname)
|
|
local pt = pointed_thing
|
|
-- check if pointing at a node
|
|
if not pt then
|
|
return itemstack
|
|
end
|
|
if pt.type ~= 'node' then
|
|
return itemstack
|
|
end
|
|
|
|
local under = minetest.get_node(pt.under)
|
|
local above = minetest.get_node(pt.above)
|
|
|
|
local player_name = placer and placer:get_player_name() or ''
|
|
|
|
if minetest.is_protected(pt.under, player_name) then
|
|
minetest.record_protection_violation(pt.under, player_name)
|
|
return itemstack
|
|
end
|
|
if minetest.is_protected(pt.above, player_name) then
|
|
minetest.record_protection_violation(pt.above, player_name)
|
|
return itemstack
|
|
end
|
|
|
|
-- return if any of the nodes is not registered
|
|
if not minetest.registered_nodes[under.name] then
|
|
return itemstack
|
|
end
|
|
if not minetest.registered_nodes[above.name] then
|
|
return itemstack
|
|
end
|
|
|
|
-- check if pointing at the top of the node
|
|
if pt.above.y ~= pt.under.y + 1 then
|
|
return itemstack
|
|
end
|
|
|
|
-- check if you can replace the node above the pointed node
|
|
if not minetest.registered_nodes[above.name].buildable_to then
|
|
return itemstack
|
|
end
|
|
|
|
-- check if pointing at soil
|
|
if minetest.get_item_group(under.name, 'soil') < 2 then
|
|
return itemstack
|
|
end
|
|
|
|
-- add the node and remove 1 item from the itemstack
|
|
x_farming.log_player_action(placer, 'places node', plantname, 'at', pt.above)
|
|
minetest.add_node(pt.above, { name = plantname, param2 = 1 })
|
|
x_farming.tick(pt.above)
|
|
if not minetest.is_creative_enabled(player_name) then
|
|
itemstack:take_item()
|
|
end
|
|
|
|
return itemstack
|
|
end
|
|
|
|
x_farming.grow_plant = function(pos, elapsed)
|
|
local node = minetest.get_node(pos)
|
|
local name = node.name
|
|
local def = minetest.registered_nodes[name]
|
|
|
|
if not def.next_plant then
|
|
-- disable timer for fully grown plant
|
|
return
|
|
end
|
|
|
|
-- grow seed
|
|
if minetest.get_item_group(node.name, 'seed') and def.fertility then
|
|
local soil_node = minetest.get_node_or_nil({ x = pos.x, y = pos.y - 1, z = pos.z })
|
|
if not soil_node then
|
|
x_farming.tick_again(pos)
|
|
return
|
|
end
|
|
-- omitted is a check for light, we assume seeds can germinate in the dark.
|
|
for _, v in pairs(def.fertility) do
|
|
if minetest.get_item_group(soil_node.name, v) ~= 0 or string.find(soil_node.name, 'mcl_farming:soil') then
|
|
local placenode = { name = def.next_plant }
|
|
if def.place_param2 then
|
|
placenode.param2 = def.place_param2
|
|
end
|
|
minetest.swap_node(pos, placenode)
|
|
if minetest.registered_nodes[def.next_plant].next_plant then
|
|
x_farming.tick(pos)
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
return
|
|
end
|
|
|
|
-- check if on wet soil
|
|
local below = minetest.get_node({ x = pos.x, y = pos.y - 1, z = pos.z })
|
|
if minetest.get_item_group(below.name, 'soil') < 3 then
|
|
x_farming.tick_again(pos)
|
|
return
|
|
end
|
|
|
|
-- check light
|
|
local light = minetest.get_node_light(pos)
|
|
if not light or light < def.minlight or light > def.maxlight then
|
|
x_farming.tick_again(pos)
|
|
return
|
|
end
|
|
|
|
-- grow
|
|
local placenode = { name = def.next_plant }
|
|
if def.place_param2 then
|
|
placenode.param2 = def.place_param2
|
|
end
|
|
minetest.swap_node(pos, placenode)
|
|
|
|
-- new timer needed?
|
|
if minetest.registered_nodes[def.next_plant].next_plant then
|
|
x_farming.tick(pos)
|
|
end
|
|
return
|
|
end
|
|
|
|
-- Register plants
|
|
x_farming.register_plant = function(name, def)
|
|
local mname = name:split(':')[1]
|
|
local pname = name:split(':')[2]
|
|
|
|
-- Check def table
|
|
if not def.description then
|
|
def.description = S('Seed')
|
|
end
|
|
if not def.harvest_description then
|
|
def.harvest_description = pname:gsub('^%l', string.upper)
|
|
end
|
|
if not def.inventory_image then
|
|
def.inventory_image = 'unknown_item.png'
|
|
end
|
|
if not def.steps then
|
|
return nil
|
|
end
|
|
if not def.minlight then
|
|
def.minlight = 1
|
|
end
|
|
if not def.maxlight then
|
|
def.maxlight = 14
|
|
end
|
|
if not def.fertility then
|
|
def.fertility = {}
|
|
end
|
|
|
|
x_farming.registered_plants[pname] = def
|
|
|
|
-- Register seed
|
|
local lbm_nodes = { mname .. ':seed_' .. pname }
|
|
local g = {
|
|
-- MTG
|
|
seed = 1,
|
|
snappy = 3,
|
|
attached_node = 1,
|
|
flammable = 2,
|
|
-- MCL
|
|
handy = 1,
|
|
shearsy = 1,
|
|
deco_block = 1,
|
|
dig_by_water = 1,
|
|
destroy_by_lava_flow = 1,
|
|
dig_by_piston = 1
|
|
}
|
|
|
|
for k, v in pairs(def.fertility) do
|
|
g[v] = 1
|
|
end
|
|
|
|
if def.groups then
|
|
for group, value in pairs(def.groups) do
|
|
g[group] = value
|
|
end
|
|
end
|
|
|
|
minetest.register_node(':' .. mname .. ':seed_' .. pname, {
|
|
description = def.description,
|
|
tiles = def.tiles or { def.inventory_image },
|
|
inventory_image = def.inventory_image,
|
|
wield_image = def.inventory_image,
|
|
drawtype = def.drawtype or 'signlike',
|
|
groups = g,
|
|
paramtype = 'light',
|
|
paramtype2 = def.paramtype2 or 'wallmounted',
|
|
place_param2 = def.place_param2 or nil, -- this isn't actually used for placement
|
|
walkable = false,
|
|
sunlight_propagates = true,
|
|
selection_box = def.selection_box or {
|
|
type = 'fixed',
|
|
fixed = { -0.5, -0.5, -0.5, 0.5, -5 / 16, 0.5 },
|
|
},
|
|
fertility = def.fertility,
|
|
sounds = x_farming.node_sound_grass_defaults(),
|
|
special_tiles = def.special_tiles and { { name = def.special_tiles, tileable_vertical = true } } or nil,
|
|
visual_scale = def.visual_scale or 1,
|
|
node_dig_prediction = def.node_dig_prediction or '',
|
|
node_placement_prediction = def.node_placement_prediction or nil,
|
|
|
|
on_place = def.on_place or function(itemstack, placer, pointed_thing)
|
|
local under = pointed_thing.under
|
|
local node = minetest.get_node(under)
|
|
local udef = minetest.registered_nodes[node.name]
|
|
if udef and udef.on_rightclick and
|
|
not (placer and placer:is_player() and
|
|
placer:get_player_control().sneak)
|
|
then
|
|
return udef.on_rightclick(under, node, placer, itemstack, pointed_thing) or itemstack
|
|
end
|
|
|
|
return x_farming.place_seed(itemstack, placer, pointed_thing, mname .. ':seed_' .. pname)
|
|
end,
|
|
after_destruct = def.after_destruct or nil,
|
|
next_plant = mname .. ':' .. pname .. '_1',
|
|
on_timer = def.on_timer or x_farming.grow_plant,
|
|
minlight = def.minlight,
|
|
maxlight = def.maxlight,
|
|
_mcl_blast_resistance = 0,
|
|
_mcl_hardness = 0,
|
|
})
|
|
|
|
-- Register harvest
|
|
minetest.register_craftitem(':' .. mname .. ':' .. pname, {
|
|
description = def.harvest_description,
|
|
inventory_image = mname .. '_' .. pname .. '.png',
|
|
groups = def.groups or { flammable = 2 },
|
|
})
|
|
|
|
-- Register growing steps
|
|
for i = 1, def.steps do
|
|
local base_rarity = 1
|
|
if def.steps ~= 1 then
|
|
base_rarity = 8 - (i - 1) * 7 / (def.steps - 1)
|
|
end
|
|
local drop = {
|
|
items = {
|
|
{items = { mname .. ':' .. pname }, rarity = base_rarity },
|
|
{items = { mname .. ':' .. pname }, rarity = base_rarity * 2 },
|
|
{items = { mname .. ':seed_' .. pname }, rarity = base_rarity },
|
|
{items = { mname .. ':seed_' .. pname }, rarity = base_rarity * 2 },
|
|
}
|
|
}
|
|
local nodegroups = {
|
|
-- MTG
|
|
snappy = 3,
|
|
flammable = 2,
|
|
plant = 1,
|
|
not_in_creative_inventory = 1,
|
|
attached_node = 1,
|
|
-- MCL
|
|
handy = 1,
|
|
shearsy = 1,
|
|
deco_block = 1,
|
|
dig_by_water = 1,
|
|
destroy_by_lava_flow = 1,
|
|
dig_by_piston = 1
|
|
}
|
|
nodegroups[pname] = i
|
|
|
|
if def.groups then
|
|
for group, value in pairs(def.groups) do
|
|
nodegroups[group] = value
|
|
end
|
|
end
|
|
|
|
local next_plant = nil
|
|
|
|
if i < def.steps then
|
|
next_plant = mname .. ':' .. pname .. '_' .. (i + 1)
|
|
lbm_nodes[#lbm_nodes + 1] = mname .. ':' .. pname .. '_' .. i
|
|
end
|
|
|
|
local _buildable_to = true
|
|
|
|
if def.buildable_to ~= nil then
|
|
_buildable_to = def.buildable_to
|
|
end
|
|
|
|
minetest.register_node(':' .. mname .. ':' .. pname .. '_' .. i, {
|
|
drawtype = def.drawtype or 'plantlike',
|
|
waving = 1,
|
|
tiles = def.tiles or { mname .. '_' .. pname .. '_' .. i .. '.png' },
|
|
special_tiles = def.special_tiles and { { name = mname .. '_' .. pname .. '_' .. i .. '.png', tileable_vertical = true } } or nil,
|
|
paramtype = 'light',
|
|
paramtype2 = def.paramtype2 or nil,
|
|
place_param2 = def.place_param2 or nil,
|
|
visual_scale = def.visual_scale or 1,
|
|
node_dig_prediction = def.node_dig_prediction or '',
|
|
node_placement_prediction = def.node_placement_prediction or nil,
|
|
walkable = false,
|
|
buildable_to = _buildable_to,
|
|
drop = drop,
|
|
selection_box = def['selection_box_' .. i] and def['selection_box_' .. i] or {
|
|
type = 'fixed',
|
|
fixed = { -0.5, -0.5, -0.5, 0.5, -5 / 16, 0.5 },
|
|
},
|
|
groups = nodegroups,
|
|
sounds = x_farming.node_sound_leaves_defaults(),
|
|
next_plant = next_plant,
|
|
on_timer = def.on_timer or x_farming.grow_plant,
|
|
minlight = def.minlight,
|
|
maxlight = def.maxlight,
|
|
_mcl_blast_resistance = 0,
|
|
_mcl_hardness = 0,
|
|
after_destruct = def.after_destruct or nil,
|
|
})
|
|
end
|
|
|
|
-- replacement LBM for pre-nodetimer plants
|
|
minetest.register_lbm({
|
|
name = ':' .. mname .. ':start_nodetimer_' .. pname,
|
|
nodenames = lbm_nodes,
|
|
action = function(pos, node)
|
|
x_farming.tick_again(pos)
|
|
end,
|
|
})
|
|
|
|
-- Return
|
|
local r = {
|
|
seed = mname .. ':seed_' .. pname,
|
|
harvest = mname .. ':' .. pname
|
|
}
|
|
return r
|
|
end
|
|
|
|
---grow blocks next to the plant
|
|
function x_farming.grow_block(pos, elapsed)
|
|
local node = minetest.get_node(pos)
|
|
local random_pos = false
|
|
local spawn_positions = {}
|
|
local right_pos = { x = pos.x + 1, y = pos.y, z = pos.z }
|
|
local front_pos = { x = pos.x, y = pos.y, z = pos.z + 1 }
|
|
local left_pos = { x = pos.x - 1, y = pos.y, z = pos.z }
|
|
local back_pos = { x = pos.x, y = pos.y, z = pos.z - 1 }
|
|
local right = minetest.get_node(right_pos)
|
|
local front = minetest.get_node(front_pos)
|
|
local left = minetest.get_node(left_pos)
|
|
local back = minetest.get_node(back_pos)
|
|
local def = minetest.registered_nodes[node.name]
|
|
|
|
local children = {}
|
|
|
|
---look for fruits around the stem
|
|
if (right.name == def.next_plant) then
|
|
children.right = right_pos
|
|
end
|
|
if (front.name == def.next_plant) then
|
|
children.front = front_pos
|
|
end
|
|
if (left.name == def.next_plant) then
|
|
children.left = left_pos
|
|
end
|
|
if (back.name == def.next_plant) then
|
|
children.back = back_pos
|
|
end
|
|
|
|
---check if the fruit belongs to this stem
|
|
for side, child_pos in pairs(children) do
|
|
|
|
local parent_pos_from_child = x_farming.meta_get_str('parent', child_pos)
|
|
|
|
---disable timer for fully grown plant - fruit for this stem already exists
|
|
if minetest.pos_to_string(pos) == parent_pos_from_child then
|
|
return
|
|
end
|
|
end
|
|
|
|
---make sure that at least one side of the plant has space to put fruit
|
|
if right.name == 'air' then
|
|
table.insert(spawn_positions, right_pos)
|
|
end
|
|
if front.name == 'air' then
|
|
table.insert(spawn_positions, front_pos)
|
|
end
|
|
if left.name == 'air' then
|
|
table.insert(spawn_positions, left_pos)
|
|
end
|
|
if back.name == 'air' then
|
|
table.insert(spawn_positions, back_pos)
|
|
end
|
|
|
|
---plant is closed from all sides
|
|
if #spawn_positions < 1 then
|
|
x_farming.tick_block_short(pos)
|
|
return
|
|
else
|
|
---pick random from the open sides
|
|
local pick_random
|
|
|
|
if #spawn_positions == 1 then
|
|
pick_random = #spawn_positions
|
|
else
|
|
pick_random = math.random(1, #spawn_positions)
|
|
end
|
|
|
|
for k, v in pairs(spawn_positions) do
|
|
if k == pick_random then
|
|
random_pos = v
|
|
end
|
|
end
|
|
end
|
|
|
|
---check light
|
|
local light = minetest.get_node_light(pos)
|
|
if not light or light < 13 or light > 14 then
|
|
x_farming.tick_block_short(pos)
|
|
return
|
|
end
|
|
|
|
---spawn block
|
|
if random_pos then
|
|
minetest.set_node(random_pos, { name = def.next_plant })
|
|
x_farming.meta_set_str('parent', minetest.pos_to_string(pos), random_pos)
|
|
end
|
|
return
|
|
end
|
|
|
|
function x_farming.grow_kiwi_tree(pos)
|
|
local path = minetest.get_modpath('x_farming') ..
|
|
'/schematics/x_farming_kiwi_tree_from_sapling.mts'
|
|
minetest.place_schematic({ x = pos.x - 2, y = pos.y, z = pos.z - 2 },
|
|
path, 'random', nil, false)
|
|
end
|
|
|
|
-- 'can grow' function - copy from MTG
|
|
|
|
function x_farming.can_grow(pos)
|
|
local node_under = minetest.get_node_or_nil({ x = pos.x, y = pos.y - 1, z = pos.z })
|
|
if not node_under then
|
|
return false
|
|
end
|
|
|
|
if minetest.get_item_group(node_under.name, 'soil') == 0 then
|
|
return false
|
|
end
|
|
|
|
local light_level = minetest.get_node_light(pos)
|
|
|
|
if not light_level or light_level < 13 then
|
|
return false
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
---Grow sapling
|
|
|
|
function x_farming.grow_sapling(pos)
|
|
if not x_farming.can_grow(pos) then
|
|
---try again 5 min later
|
|
minetest.get_node_timer(pos):start(300)
|
|
return
|
|
end
|
|
|
|
local node = minetest.get_node(pos)
|
|
if node.name == 'x_farming:kiwi_sapling' then
|
|
minetest.log('action', 'A sapling grows into a tree at ' ..
|
|
minetest.pos_to_string(pos))
|
|
x_farming.grow_kiwi_tree(pos)
|
|
end
|
|
end
|
|
|
|
---Grow Large Cactus
|
|
|
|
function x_farming.grow_large_cactus(pos)
|
|
local path = minetest.get_modpath('x_farming') ..
|
|
'/schematics/x_farming_large_cactus_from_seedling.mts'
|
|
minetest.place_schematic({ x = pos.x, y = pos.y, z = pos.z },
|
|
path, 'random', nil, false, 'place_center_x, place_center_z')
|
|
end
|
|
|
|
---Grow Jungle Tree
|
|
|
|
function x_farming.grow_jungle_tree(pos)
|
|
local path = minetest.get_modpath('x_farming') ..
|
|
'/schematics/x_farming_jungle_tree_with_cocoa_from_sapling.mts'
|
|
minetest.place_schematic({ x = pos.x - 2, y = pos.y - 1, z = pos.z - 2 },
|
|
path, nil, nil, false)
|
|
end
|
|
|
|
---Pine Nut Tree
|
|
|
|
function x_farming.grow_pine_nut_tree(pos)
|
|
local path = minetest.get_modpath('x_farming') ..
|
|
'/schematics/x_farming_pine_nut_tree_from_sapling.mts'
|
|
minetest.place_schematic({ x = pos.x - 2, y = pos.y, z = pos.z - 2 },
|
|
path, '0', nil, false)
|
|
end
|
|
|
|
---Christmas Tree
|
|
|
|
function x_farming.grow_christmas_tree(pos)
|
|
local path
|
|
if math.random() > 0.5 then
|
|
path = minetest.get_modpath('x_farming') .. '/schematics/x_farming_christmas_tree_large.mts'
|
|
minetest.place_schematic({ x = pos.x - 2, y = pos.y, z = pos.z - 2 }, path, '0', nil, false)
|
|
else
|
|
path = minetest.get_modpath('x_farming') .. '/schematics/x_farming_christmas_tree.mts'
|
|
minetest.place_schematic({ x = pos.x - 1, y = pos.y, z = pos.z - 1 }, path, '0', nil, false)
|
|
end
|
|
end
|
|
|
|
----
|
|
--- Crates and Bags
|
|
----
|
|
|
|
function x_farming.tick_crates(pos)
|
|
minetest.get_node_timer(pos):start(math.random(332, 572))
|
|
end
|
|
|
|
function x_farming.tick_again_crates(pos)
|
|
minetest.get_node_timer(pos):start(math.random(80, 160))
|
|
end
|
|
|
|
function x_farming.get_crate_or_bag_formspec(pos, label_copy)
|
|
local spos = pos.x .. ',' .. pos.y .. ',' .. pos.z
|
|
local hotbar_bg = ''
|
|
local list_bg = ''
|
|
|
|
for i = 0, 7, 1 do
|
|
hotbar_bg = hotbar_bg .. 'image[' .. 0 + i .. ', ' .. 4.85 .. ';1,1;x_farming_crate_ui_bg_hb_slot.png]'
|
|
end
|
|
|
|
for row = 0, 2, 1 do
|
|
for i = 0, 7, 1 do
|
|
list_bg = list_bg .. 'image[' .. 0 + i .. ',' .. 6.08 + row .. ';1,1;x_farming_crate_ui_bg_slot.png]'
|
|
end
|
|
end
|
|
|
|
local formspec = {
|
|
'size[8,9]',
|
|
'style_type[label;textcolor=#FFFFFF]',
|
|
'background[5,5;1,1;x_farming_crate_ui_bg.png;true]',
|
|
'list[nodemeta:', spos, ';main;0,0.3;8,4;]',
|
|
'list[current_player;main;0,4.85;8,1;]',
|
|
'list[current_player;main;0,6.08;8,3;8]',
|
|
'listring[nodemeta:', spos, ';main]',
|
|
'listring[current_player;main]',
|
|
'label[2,0;' .. minetest.formspec_escape(label_copy) .. ']',
|
|
list_bg,
|
|
hotbar_bg,
|
|
'image[0,0.3;1,1;x_farming_crate_ui_bg_hb_slot.png]'
|
|
}
|
|
|
|
formspec = table.concat(formspec, '')
|
|
|
|
return formspec
|
|
end
|
|
|
|
---Crate
|
|
function x_farming.register_crate(name, def)
|
|
local _def = table.copy(def) or {}
|
|
|
|
_def._custom = _def._custom or {}
|
|
|
|
local mod = "x_farming"
|
|
if name:find(":") then
|
|
local n = name:split(":")
|
|
mod = n[1]
|
|
name = n[2]
|
|
end
|
|
|
|
_def.name = 'x_farming:' .. name
|
|
_def.description = def.description or name
|
|
_def.short_description = def.short_description or def.description
|
|
_def.drawtype = 'mesh'
|
|
_def.paramtype = 'light'
|
|
_def.paramtype2 = 'facedir'
|
|
_def.mesh = 'x_farming_crate.obj'
|
|
_def.tiles = def.tiles
|
|
_def.use_texture_alpha = 'clip'
|
|
_def.sounds = def.sounds or x_farming.node_sound_wood_defaults()
|
|
_def.is_ground_content = false
|
|
_def.groups = def.groups or {
|
|
-- MTG
|
|
choppy = 2,
|
|
oddly_breakable_by_hand = 2,
|
|
-- MCL
|
|
handy = 1,
|
|
material_wood = 1,
|
|
deco_block = 1,
|
|
fire_encouragement = 3,
|
|
fire_flammability = 4,
|
|
-- ALL
|
|
not_in_creative_inventory = 1,
|
|
flammable = 2
|
|
}
|
|
_def.stack_max = def.stack_max or 1
|
|
_def.mod_origin = mod
|
|
-- MCL
|
|
_def._mcl_hardness = 0.6
|
|
_def._mcl_blast_resistance = 0.6
|
|
|
|
if _def._custom.crate_item then
|
|
x_farming.allowed_crate_items[_def._custom.crate_item] = true
|
|
end
|
|
|
|
_def.on_construct = function(pos)
|
|
local meta = minetest.get_meta(pos)
|
|
local inv = meta:get_inventory()
|
|
meta:set_string('infotext', _def.short_description)
|
|
meta:set_string('owner', '')
|
|
inv:set_size('main', 1)
|
|
end
|
|
|
|
_def.after_place_node = function(pos, placer, itemstack, pointed_thing)
|
|
local meta = minetest.get_meta(pos)
|
|
local meta_st = itemstack:get_meta()
|
|
local crate_inv = minetest.deserialize(meta_st:get_string('crate_inv'))
|
|
local inv = meta:get_inventory()
|
|
|
|
if crate_inv then
|
|
inv:add_item('main', ItemStack(crate_inv))
|
|
end
|
|
|
|
local node = minetest.get_node(pos)
|
|
|
|
meta:set_string('owner', placer:get_player_name() or '')
|
|
|
|
if not inv:is_empty('main') then
|
|
local inv_stack = inv:get_stack('main', 1)
|
|
|
|
meta:set_string('infotext', _def.short_description
|
|
.. ' (' .. S('owned by') .. ' ' .. meta:get_string('owner') .. ')\n'
|
|
.. inv_stack:get_description() .. '\n' .. S('Quantity') .. ': ' .. inv_stack:get_count())
|
|
else
|
|
local swap_node = minetest.registered_nodes['x_farming:crate_empty']
|
|
if swap_node and inv:is_empty('main') and node.name ~= swap_node.name then
|
|
minetest.swap_node(pos, { name = swap_node.name, param2 = node.param2 })
|
|
meta:set_string('infotext', swap_node.short_description
|
|
.. ' (' .. S('owned by') .. ' ' .. meta:get_string('owner') .. ')')
|
|
end
|
|
end
|
|
end
|
|
|
|
_def.on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
|
|
local p_name = clicker:get_player_name()
|
|
if minetest.is_protected(pos, p_name) then
|
|
return itemstack
|
|
end
|
|
|
|
local meta = minetest.get_meta(pos)
|
|
local inv = meta:get_inventory()
|
|
local inv_stack = inv:get_stack('main', 1)
|
|
local label_copy = _def.short_description .. ' (' .. S('owned by') .. ' ' .. meta:get_string('owner')
|
|
.. ')\n' .. inv_stack:get_description()
|
|
minetest.show_formspec(p_name, _def.name, x_farming.get_crate_or_bag_formspec(pos, label_copy))
|
|
minetest.sound_play('x_farming_wood_hit', { gain = 0.3, pos = pos, max_hear_distance = 10 }, true)
|
|
end
|
|
|
|
_def.on_blast = function(pos, intensity)
|
|
if minetest.is_protected(pos, '') then
|
|
return
|
|
end
|
|
|
|
local drops = {}
|
|
local inv = minetest.get_meta(pos):get_inventory()
|
|
local n = #drops
|
|
|
|
for i = 1, inv:get_size('main') do
|
|
local stack = inv:get_stack('main', i)
|
|
if stack:get_count() > 0 then
|
|
drops[n + 1] = stack:to_table()
|
|
n = n + 1
|
|
end
|
|
end
|
|
|
|
drops[#drops + 1] = name
|
|
minetest.remove_node(pos)
|
|
return drops
|
|
end
|
|
|
|
_def.can_dig = function(pos, player)
|
|
return not minetest.is_protected(pos, player:get_player_name())
|
|
end
|
|
|
|
_def.preserve_metadata = function(pos, oldnode, oldmeta, drops)
|
|
local meta = minetest.get_meta(pos)
|
|
local inv = meta:get_inventory()
|
|
local stack = drops[1]
|
|
local meta_drop = stack:get_meta()
|
|
|
|
if not inv:is_empty('main') then
|
|
local inv_stack = inv:get_stack('main', 1)
|
|
|
|
meta_drop:set_string('crate_inv', minetest.serialize(inv_stack:to_table()))
|
|
meta_drop:set_string('description', stack:get_description() .. '\n' .. inv_stack:get_description()
|
|
.. '\n' .. S('Quantity') .. ': ' .. inv_stack:get_count())
|
|
|
|
return
|
|
end
|
|
end
|
|
|
|
_def.allow_metadata_inventory_put = function(pos, listname, index, stack, player)
|
|
local st_name = stack:get_name()
|
|
|
|
if minetest.is_protected(pos, player:get_player_name())
|
|
or (
|
|
not x_farming.allowed_crate_items[st_name]
|
|
and minetest.get_item_group(st_name, 'fish') == 0
|
|
)
|
|
then
|
|
return 0
|
|
end
|
|
|
|
return stack:get_count()
|
|
end
|
|
|
|
_def.allow_metadata_inventory_take = function(pos, listname, index, stack, player)
|
|
local st_name = stack:get_name()
|
|
|
|
if minetest.is_protected(pos, player:get_player_name())
|
|
or (
|
|
not x_farming.allowed_crate_items[st_name]
|
|
and minetest.get_item_group(st_name, 'fish') == 0
|
|
)
|
|
then
|
|
return 0
|
|
end
|
|
|
|
return stack:get_count()
|
|
end
|
|
|
|
_def.on_metadata_inventory_put = function(pos, listname, index, stack, player)
|
|
local stack_name = stack:get_name()
|
|
|
|
if not stack_name or stack_name == '' then
|
|
return
|
|
end
|
|
|
|
local meta = minetest.get_meta(pos)
|
|
local inv = meta:get_inventory()
|
|
local node = minetest.get_node(pos)
|
|
local inv_stack = inv:get_stack('main', 1)
|
|
local split_name = stack_name:split(':')
|
|
|
|
if minetest.get_item_group(stack_name, 'fish') ~= 0 then
|
|
split_name = { 'x_farming', 'fish' }
|
|
end
|
|
|
|
if stack_name == 'x_farming:cotton' then
|
|
split_name = { 'x_farming', 'cotton2' }
|
|
end
|
|
|
|
local swap_node = minetest.registered_nodes['x_farming:crate_' .. split_name[2] .. '_3']
|
|
|
|
if not swap_node then
|
|
return
|
|
end
|
|
|
|
if not inv:is_empty(listname) and node.name ~= swap_node.name then
|
|
local p_name = player:get_player_name()
|
|
|
|
minetest.swap_node(pos, { name = swap_node.name, param2 = node.param2 })
|
|
|
|
local label_copy = swap_node.short_description .. ' (' .. S('owned by') .. ' ' .. meta:get_string('owner') .. ')\n'
|
|
.. inv_stack:get_description()
|
|
|
|
minetest.show_formspec(p_name, _def.name, x_farming.get_crate_or_bag_formspec(pos, label_copy))
|
|
end
|
|
|
|
meta:set_string('infotext', swap_node.short_description .. ' (' .. S('owned by')
|
|
.. ' ' .. meta:get_string('owner') .. ')\n'
|
|
.. inv_stack:get_description() .. '\n' .. S('Quantity') .. ': ' .. inv_stack:get_count())
|
|
end
|
|
|
|
_def.on_metadata_inventory_take = function(pos, listname, index, stack, player)
|
|
local meta = minetest.get_meta(pos)
|
|
local inv = meta:get_inventory()
|
|
local inv_stack = inv:get_stack('main', 1)
|
|
local node = minetest.get_node(pos)
|
|
|
|
if inv:is_empty(listname) then
|
|
local p_name = player:get_player_name()
|
|
local swap_node = minetest.registered_nodes['x_farming:crate_empty']
|
|
|
|
if swap_node then
|
|
minetest.swap_node(pos, { name = swap_node.name, param2 = node.param2 })
|
|
meta:set_string('infotext', swap_node.short_description
|
|
.. ' (' .. S('owned by') .. ' ' .. meta:get_string('owner') .. ')')
|
|
|
|
local label_copy = swap_node.short_description .. ' (' .. S('owned by') .. ' '
|
|
.. meta:get_string('owner') .. ')\n' .. inv_stack:get_description()
|
|
|
|
minetest.show_formspec(p_name, _def.name, x_farming.get_crate_or_bag_formspec(pos, label_copy))
|
|
end
|
|
else
|
|
local node_def = minetest.registered_nodes[node.name]
|
|
|
|
if node_def then
|
|
meta:set_string('infotext', node_def.short_description
|
|
.. ' (' .. S('owned by') .. ' ' .. meta:get_string('owner') .. ')\n'
|
|
.. inv_stack:get_description() .. '\n' .. S('Quantity') .. ': ' .. inv_stack:get_count())
|
|
end
|
|
end
|
|
end
|
|
|
|
_def.on_timer = function(pos, elapsed)
|
|
local pos_above = { x = pos.x, y = pos.y + 1, z = pos.z }
|
|
local node_above = minetest.get_node(pos_above)
|
|
|
|
if not node_above then
|
|
x_farming.tick_again_crates(pos)
|
|
return
|
|
end
|
|
|
|
if node_above.name ~= 'air' then
|
|
x_farming.tick_again_crates(pos)
|
|
return
|
|
end
|
|
|
|
local rand1 = math.random(1, 2) / 10
|
|
|
|
minetest.add_particlespawner({
|
|
amount = 60,
|
|
time = 15,
|
|
minpos = { x = pos_above.x - 0.1, y = pos_above.y - 0.3, z = pos_above.z - 0.1 },
|
|
maxpos = { x = pos_above.x + 0.1, y = pos_above.y + 0.4, z = pos_above.z + 0.1 },
|
|
minvel = { x = rand1 * -1, y = rand1 * -1, z = rand1 * -1 },
|
|
maxvel = { x = rand1, y = rand1, z = rand1 },
|
|
minacc = { x = rand1 * -1, y = rand1 * -1, z = rand1 * -1 },
|
|
maxacc = { x = rand1, y = rand1, z = rand1 },
|
|
minexptime = 1,
|
|
maxexptime = 1.5,
|
|
minsize = 0.1,
|
|
maxsize = 0.3,
|
|
texture = 'x_farming_fly.png',
|
|
collisiondetection = true,
|
|
object_collision = true
|
|
})
|
|
|
|
x_farming.tick_crates(pos)
|
|
end
|
|
|
|
x_farming.registered_crates[_def.name] = _def
|
|
|
|
if _def.name ~= 'x_farming:crate_empty' then
|
|
table.insert(x_farming.lbm_nodenames_crates, _def.name)
|
|
end
|
|
|
|
minetest.register_node(_def.name, _def)
|
|
end
|
|
|
|
---Bag
|
|
function x_farming.register_bag(name, def)
|
|
local _def = table.copy(def) or {}
|
|
|
|
_def._custom = _def._custom or {}
|
|
|
|
_def.name = 'x_farming:' .. name
|
|
_def.description = def.description or name
|
|
_def.short_description = def.short_description or def.description
|
|
_def.drawtype = 'mesh'
|
|
_def.paramtype = 'light'
|
|
_def.paramtype2 = 'facedir'
|
|
_def.mesh = 'x_farming_bag.obj'
|
|
_def.tiles = def.tiles
|
|
_def.use_texture_alpha = 'clip'
|
|
_def.sounds = def.sounds or x_farming.node_sound_sand_defaults()
|
|
_def.is_ground_content = false
|
|
_def.groups = def.groups or {
|
|
-- MTG
|
|
choppy = 2,
|
|
oddly_breakable_by_hand = 2,
|
|
-- MCL
|
|
handy = 1,
|
|
building_block = 1,
|
|
deco_block = 1,
|
|
-- ALL
|
|
not_in_creative_inventory = 1,
|
|
flammable = 2
|
|
}
|
|
-- MCL
|
|
_def._mcl_hardness = 0.6
|
|
_def._mcl_blast_resistance = 0.6
|
|
_def.stack_max = def.stack_max or 1
|
|
_def.mod_origin = 'x_farming'
|
|
|
|
if _def._custom.bag_item then
|
|
x_farming.allowed_bag_items[_def._custom.bag_item] = true
|
|
end
|
|
|
|
_def.on_construct = function(pos)
|
|
local meta = minetest.get_meta(pos)
|
|
local inv = meta:get_inventory()
|
|
meta:set_string('infotext', _def.short_description)
|
|
meta:set_string('owner', '')
|
|
inv:set_size('main', 1)
|
|
end
|
|
|
|
_def.after_place_node = function(pos, placer, itemstack, pointed_thing)
|
|
local meta = minetest.get_meta(pos)
|
|
local meta_st = itemstack:get_meta()
|
|
local bag_inv = minetest.deserialize(meta_st:get_string('bag_inv'))
|
|
local inv = meta:get_inventory()
|
|
|
|
if bag_inv then
|
|
inv:add_item('main', ItemStack(bag_inv))
|
|
end
|
|
|
|
local node = minetest.get_node(pos)
|
|
|
|
meta:set_string('owner', placer:get_player_name() or '')
|
|
|
|
if not inv:is_empty('main') then
|
|
local inv_stack = inv:get_stack('main', 1)
|
|
|
|
meta:set_string('infotext', _def.short_description .. ' (' .. S('owned by') .. ' '
|
|
.. meta:get_string('owner') .. ')\n' .. inv_stack:get_description()
|
|
.. '\n' .. S('Quantity') .. ': ' .. inv_stack:get_count())
|
|
else
|
|
local swap_node = minetest.registered_nodes['x_farming:bag_empty']
|
|
if swap_node and inv:is_empty('main') and node.name ~= swap_node.name then
|
|
minetest.swap_node(pos, { name = swap_node.name, param2 = node.param2 })
|
|
meta:set_string('infotext', swap_node.short_description
|
|
.. ' (' .. S('owned by') .. ' ' .. meta:get_string('owner') .. ')')
|
|
end
|
|
end
|
|
end
|
|
|
|
_def.on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
|
|
local p_name = clicker:get_player_name()
|
|
|
|
if minetest.is_protected(pos, p_name) then
|
|
return itemstack
|
|
end
|
|
|
|
local meta = minetest.get_meta(pos)
|
|
local inv = meta:get_inventory()
|
|
local inv_stack = inv:get_stack('main', 1)
|
|
local label_copy = _def.short_description .. ' (' .. S('owned by') .. ' ' .. meta:get_string('owner') .. ')\n'
|
|
.. inv_stack:get_description()
|
|
minetest.show_formspec(p_name, _def.name, x_farming.get_crate_or_bag_formspec(pos, label_copy))
|
|
minetest.sound_play('x_farming_sand_footstep', { gain = 0.3, pos = pos, max_hear_distance = 10 }, true)
|
|
end
|
|
|
|
_def.on_blast = function(pos, intensity)
|
|
if minetest.is_protected(pos, '') then
|
|
return
|
|
end
|
|
|
|
local drops = {}
|
|
local inv = minetest.get_meta(pos):get_inventory()
|
|
local n = #drops
|
|
|
|
for i = 1, inv:get_size('main') do
|
|
local stack = inv:get_stack('main', i)
|
|
if stack:get_count() > 0 then
|
|
drops[n + 1] = stack:to_table()
|
|
n = n + 1
|
|
end
|
|
end
|
|
|
|
drops[#drops + 1] = name
|
|
minetest.remove_node(pos)
|
|
return drops
|
|
end
|
|
|
|
_def.can_dig = function(pos, player)
|
|
return not minetest.is_protected(pos, player:get_player_name())
|
|
end
|
|
|
|
_def.preserve_metadata = function(pos, oldnode, oldmeta, drops)
|
|
local meta = minetest.get_meta(pos)
|
|
local inv = meta:get_inventory()
|
|
local stack = drops[1]
|
|
local meta_drop = stack:get_meta()
|
|
|
|
if not inv:is_empty('main') then
|
|
local inv_stack = inv:get_stack('main', 1)
|
|
|
|
meta_drop:set_string('bag_inv', minetest.serialize(inv_stack:to_table()))
|
|
meta_drop:set_string('description', stack:get_description() .. '\n'
|
|
.. inv_stack:get_description() .. '\n' .. S('Quantity') .. ': ' .. inv_stack:get_count())
|
|
|
|
return
|
|
end
|
|
end
|
|
|
|
_def.allow_metadata_inventory_put = function(pos, listname, index, stack, player)
|
|
if minetest.is_protected(pos, player:get_player_name()) or not x_farming.allowed_bag_items[stack:get_name()] then
|
|
return 0
|
|
end
|
|
|
|
return stack:get_count()
|
|
end
|
|
|
|
_def.allow_metadata_inventory_take = function(pos, listname, index, stack, player)
|
|
if minetest.is_protected(pos, player:get_player_name()) or not x_farming.allowed_bag_items[stack:get_name()] then
|
|
return 0
|
|
end
|
|
|
|
return stack:get_count()
|
|
end
|
|
|
|
_def.on_metadata_inventory_put = function(pos, listname, index, stack, player)
|
|
local stack_name = stack:get_name()
|
|
|
|
if not stack_name or stack_name == '' then
|
|
return
|
|
end
|
|
|
|
local meta = minetest.get_meta(pos)
|
|
local inv = meta:get_inventory()
|
|
local split_name = stack_name:split(':')
|
|
local node = minetest.get_node(pos)
|
|
local swap_node = minetest.registered_nodes['x_farming:bag_' .. split_name[2]]
|
|
local inv_stack = inv:get_stack('main', 1)
|
|
|
|
if not swap_node then
|
|
return
|
|
end
|
|
|
|
if not inv:is_empty(listname) and node.name ~= swap_node.name then
|
|
local p_name = player:get_player_name()
|
|
|
|
minetest.swap_node(pos, { name = swap_node.name, param2 = node.param2 })
|
|
|
|
local label_copy = swap_node.short_description .. ' (' .. S('owned by') .. ' ' .. meta:get_string('owner') .. ')\n'
|
|
.. inv_stack:get_description()
|
|
|
|
minetest.show_formspec(p_name, _def.name, x_farming.get_crate_or_bag_formspec(pos, label_copy))
|
|
end
|
|
|
|
meta:set_string('infotext', swap_node.short_description .. ' (' .. S('owned by')
|
|
.. ' ' .. meta:get_string('owner') .. ')\n'
|
|
.. inv_stack:get_description() .. '\n' .. S('Quantity') .. ': ' .. inv_stack:get_count())
|
|
end
|
|
|
|
_def.on_metadata_inventory_take = function(pos, listname, index, stack, player)
|
|
local meta = minetest.get_meta(pos)
|
|
local inv = meta:get_inventory()
|
|
local inv_stack = inv:get_stack('main', 1)
|
|
local node = minetest.get_node(pos)
|
|
|
|
if inv:is_empty(listname) then
|
|
local p_name = player:get_player_name()
|
|
local swap_node = minetest.registered_nodes['x_farming:bag_empty']
|
|
|
|
if swap_node then
|
|
minetest.swap_node(pos, { name = swap_node.name, param2 = node.param2 })
|
|
meta:set_string('infotext', swap_node.short_description
|
|
.. ' (' .. S('owned by') .. ' ' .. meta:get_string('owner') .. ')')
|
|
|
|
local label_copy = swap_node.short_description
|
|
.. ' (' .. S('owned by') .. ' ' .. meta:get_string('owner') .. ')\n' .. inv_stack:get_description()
|
|
|
|
minetest.show_formspec(p_name, _def.name, x_farming.get_crate_or_bag_formspec(pos, label_copy))
|
|
end
|
|
else
|
|
local node_def = minetest.registered_nodes[node.name]
|
|
|
|
if node_def then
|
|
meta:set_string('infotext', node_def.short_description .. ' (' .. S('owned by') .. ' '
|
|
.. meta:get_string('owner') .. ')\n' .. inv_stack:get_description()
|
|
.. '\n' .. S('Quantity') .. ': ' .. inv_stack:get_count())
|
|
end
|
|
end
|
|
end
|
|
|
|
minetest.register_node(_def.name, _def)
|
|
end
|
|
|
|
--
|
|
---Bonemeal
|
|
--
|
|
|
|
------------
|
|
---Main API for x_bonemeal Mod
|
|
---@author Juraj Vajda
|
|
---@license GNU LGPL 2.1
|
|
----
|
|
|
|
---Get creative mode setting from minetest.conf
|
|
local creative_mod_cache = minetest.settings:get_bool('creative_mode')
|
|
|
|
---Check if creating mode is enabled or player has creative privs
|
|
---@param name string name
|
|
---@return boolean
|
|
function x_farming.x_bonemeal.is_creative(name)
|
|
return creative_mod_cache or minetest.check_player_privs(name, { creative = true })
|
|
end
|
|
|
|
---Check if node has a soil below its self
|
|
---@param under Vector of position
|
|
---@return boolean
|
|
function x_farming.x_bonemeal.is_on_soil(under)
|
|
local below = minetest.get_node_or_nil({ x = under.x, y = under.y - 1, z = under.z })
|
|
|
|
if not below then
|
|
return false
|
|
end
|
|
|
|
if minetest.get_item_group(below.name, 'soil') == 0 and below.name ~= 'mcl_farming:soil_wet' then
|
|
return false
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
---Check if node has a sand below its self
|
|
---@param under Vector of position
|
|
---@return boolean
|
|
function x_farming.x_bonemeal.is_on_sand(under)
|
|
local below = minetest.get_node_or_nil({ x = under.x, y = under.y - 1, z = under.z })
|
|
|
|
if not below then
|
|
return false
|
|
end
|
|
|
|
if minetest.get_item_group(below.name, 'sand') == 0
|
|
and minetest.get_item_group(below.name, 'everness_sand') == 0
|
|
then
|
|
return false
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
---Growth steps for farming plants, there is no way of getting them dynamically,
|
|
--- so they are defined in the local table variable
|
|
local farming_steps = {
|
|
['farming:wheat'] = 8,
|
|
['farming:cotton'] = 8,
|
|
['x_farming:coffee'] = 5,
|
|
['x_farming:corn'] = 10,
|
|
['x_farming:obsidian_wart'] = 6,
|
|
['x_farming:melon'] = 8,
|
|
['x_farming:pumpkin'] = 8,
|
|
['x_farming:carrot'] = 8,
|
|
['x_farming:potato'] = 8,
|
|
['x_farming:beetroot'] = 8,
|
|
['x_farming:strawberry'] = 4,
|
|
['x_farming:stevia'] = 8,
|
|
['x_farming:soybean'] = 7,
|
|
['x_farming:salt'] = 7,
|
|
['x_farming:barley'] = 8,
|
|
['x_farming:cotton'] = 8,
|
|
}
|
|
|
|
---Particle and sound effect after the bone meal is successfully used
|
|
---@param pos Vector containing position
|
|
function x_farming.x_bonemeal.particle_effect(pos)
|
|
minetest.sound_play('x_farming_x_bonemeal_grow', {
|
|
pos = pos,
|
|
gain = 0.5,
|
|
})
|
|
|
|
minetest.add_particlespawner({
|
|
amount = 6,
|
|
time = 3,
|
|
minpos = { x = pos.x - 0.4, y = pos.y - 0.4, z = pos.z - 0.4 },
|
|
maxpos = { x = pos.x + 0.4, y = pos.y, z = pos.z + 0.4 },
|
|
minvel = { x = 0, y = 0, z = 0 },
|
|
maxvel = { x = 0, y = 0.1, z = 0 },
|
|
minacc = vector.new({ x = 0, y = 0, z = 0 }),
|
|
maxacc = vector.new({ x = 0, y = 0.1, z = 0 }),
|
|
minexptime = 2,
|
|
maxexptime = 3,
|
|
minsize = 1,
|
|
maxsize = 3,
|
|
texture = 'x_farming_x_bonemeal_particles.png',
|
|
animation = {
|
|
type = 'vertical_frames',
|
|
aspect_w = 8,
|
|
aspect_h = 8,
|
|
length = 3,
|
|
},
|
|
})
|
|
end
|
|
|
|
function x_farming.x_bonemeal.tableContains(table, value)
|
|
local found = false
|
|
|
|
if not table or type(table) ~= 'table' then
|
|
return found
|
|
end
|
|
|
|
for k, v in ipairs(table) do
|
|
if v == value then
|
|
found = true
|
|
break
|
|
end
|
|
end
|
|
|
|
return found
|
|
end
|
|
|
|
function x_farming.x_bonemeal.groupContains(groups, fertility, value)
|
|
local found = false
|
|
|
|
if not groups or type(groups) ~= 'table' then
|
|
return found
|
|
end
|
|
|
|
if groups[fertility] and groups[fertility] == value then
|
|
found = true
|
|
end
|
|
|
|
return found
|
|
end
|
|
|
|
---Handle growth of decorations based on biome
|
|
---@param itemstack ItemStack
|
|
---@param user ObjectRef | nil
|
|
---@param pointed_thing PointedThingDef
|
|
---@return { ['success']: boolean, ['itemstack']: ItemStack }
|
|
function x_farming.x_bonemeal.grow_grass_and_flowers(itemstack, user, pointed_thing)
|
|
local result = {
|
|
success = false,
|
|
itemstack = itemstack
|
|
}
|
|
local node = minetest.get_node(pointed_thing.under)
|
|
|
|
if not node then
|
|
return result
|
|
end
|
|
|
|
local pos0 = vector.subtract(pointed_thing.under, 3)
|
|
local pos1 = vector.add(pointed_thing.under, 3)
|
|
local biome_data = minetest.get_biome_data(pointed_thing.under)
|
|
|
|
if not biome_data then
|
|
return result
|
|
end
|
|
|
|
local biome_name = minetest.get_biome_name(biome_data.biome)
|
|
|
|
if not biome_name then
|
|
return result
|
|
end
|
|
|
|
local random_number = math.random(2, 6)
|
|
local registered_decorations_filtered = {}
|
|
---@type ItemStack | nil
|
|
local returned_itemstack
|
|
local node_def = minetest.registered_nodes[node.name]
|
|
local below_water = false
|
|
local floats_on_water = false
|
|
local node_in_decor = false
|
|
local positions_dirty
|
|
local positions = {}
|
|
local decor_place_on = {}
|
|
-- print('biome_name', biome_name)
|
|
|
|
---check 1 node below pointed node (floats on water)
|
|
local test_node = minetest.get_node({
|
|
x = pointed_thing.under.x,
|
|
y = pointed_thing.under.y - 1,
|
|
z = pointed_thing.under.z
|
|
})
|
|
local test_node_def = minetest.registered_nodes[test_node.name]
|
|
|
|
if test_node_def
|
|
and test_node_def.liquidtype == 'source'
|
|
and minetest.get_item_group(test_node_def.name, 'water') > 0
|
|
then
|
|
floats_on_water = true
|
|
end
|
|
|
|
---check 2 nodes above pointed nodes (below water)
|
|
local water_nodes_above = 0
|
|
for i = 1, 2 do
|
|
local test_node2 = minetest.get_node({
|
|
x = pointed_thing.under.x,
|
|
y = pointed_thing.under.y + i,
|
|
z = pointed_thing.under.z
|
|
})
|
|
local test_node_def2 = minetest.registered_nodes[test_node2.name]
|
|
|
|
if test_node_def2
|
|
and test_node_def2.liquidtype == 'source'
|
|
and minetest.get_item_group(test_node_def2.name, 'water') > 0
|
|
then
|
|
water_nodes_above = water_nodes_above + 1
|
|
end
|
|
end
|
|
|
|
if water_nodes_above == 2 then
|
|
below_water = true
|
|
end
|
|
|
|
if below_water then
|
|
positions_dirty = minetest.find_nodes_in_area(pos0, pos1, node.name)
|
|
elseif floats_on_water then
|
|
positions_dirty = minetest.find_nodes_in_area(pos0, pos1, 'air')
|
|
else
|
|
positions_dirty = minetest.find_nodes_in_area_under_air(pos0, pos1, node.name)
|
|
end
|
|
|
|
---find suitable decorations
|
|
for _, v in pairs(minetest.registered_decorations) do
|
|
---only for 'simple' decoration types
|
|
if v.deco_type == 'simple' then
|
|
---filter based on biome name in `biomes` table and node name in `place_on` table
|
|
if x_farming.x_bonemeal.tableContains(v.biomes, biome_name) then
|
|
table.insert(registered_decorations_filtered, v)
|
|
end
|
|
end
|
|
|
|
---clicked node is in decoration
|
|
local _decoration = v.decoration
|
|
|
|
if type(v.decoration) == 'string' then
|
|
_decoration = { v.decoration }
|
|
end
|
|
|
|
if x_farming.x_bonemeal.tableContains(_decoration, node.name) then
|
|
node_in_decor = true
|
|
end
|
|
|
|
---all nodes on which decoration can be placed on
|
|
---indexed by name
|
|
if not decor_place_on[v.place_on] then
|
|
if type(v.place_on) == 'string' then
|
|
decor_place_on[v.place_on] = true
|
|
elseif type(v.place_on) == 'table' then
|
|
for _, v2 in ipairs(v.place_on) do
|
|
decor_place_on[v2] = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
---find suitable positions
|
|
for j, pos_value in ipairs(positions_dirty) do
|
|
local node_at_pos = minetest.get_node(pos_value)
|
|
|
|
if below_water then
|
|
---below water
|
|
local water_nodes_above2 = 0
|
|
|
|
---check if 2 nodes above are water
|
|
for i = 1, 2 do
|
|
local test_node3 = minetest.get_node({ x = pos_value.x, y = pos_value.y + i, z = pos_value.z })
|
|
local test_node_def3 = minetest.registered_nodes[test_node3.name]
|
|
|
|
if test_node_def3
|
|
and test_node_def3.liquidtype == 'source'
|
|
and minetest.get_item_group(test_node_def3.name, 'water') > 0
|
|
then
|
|
water_nodes_above2 = water_nodes_above2 + 1
|
|
end
|
|
end
|
|
|
|
if water_nodes_above2 == 2 and decor_place_on[test_node.name] then
|
|
table.insert(positions, pos_value)
|
|
end
|
|
else
|
|
---above water (not on water)
|
|
if decor_place_on[node_at_pos.name] then
|
|
table.insert(positions, pos_value)
|
|
end
|
|
end
|
|
end
|
|
|
|
---find suitable positions (float on water)
|
|
if floats_on_water then
|
|
for _, pos_value in ipairs(positions_dirty) do
|
|
local node_at_pos_below = minetest.get_node({ x = pos_value.x, y = pos_value.y - 1, z = pos_value.z })
|
|
local test_node_def4 = minetest.registered_nodes[node_at_pos_below.name]
|
|
|
|
if test_node_def4
|
|
and test_node_def4.liquidtype == 'source'
|
|
and minetest.get_item_group(test_node_def4.name, 'water') > 0
|
|
then
|
|
table.insert(positions, pos_value)
|
|
end
|
|
end
|
|
end
|
|
|
|
local returned_itemstack_success = 0
|
|
|
|
---place decorations on random positions
|
|
if #positions > 0 and #registered_decorations_filtered > 0 then
|
|
for i = 1, random_number do
|
|
local idx = math.random(1, #positions)
|
|
local random_pos = positions[idx]
|
|
local random_decor = registered_decorations_filtered[math.random(1, #registered_decorations_filtered)]
|
|
local random_decor_item = random_decor.decoration
|
|
|
|
if floats_on_water and node_in_decor then
|
|
random_decor_item = node.name
|
|
elseif type(random_decor.decoration) == 'table' then
|
|
random_decor_item = random_decor.decoration[math.random(1, #random_decor.decoration)]
|
|
end
|
|
|
|
local random_decor_item_def = minetest.registered_nodes[random_decor_item]
|
|
|
|
if random_pos ~= nil and random_decor_item_def.drawtype ~= 'airlike' then
|
|
if random_decor_item_def.on_place ~= nil and node_def and not node_def.on_rightclick then
|
|
---on_place
|
|
local pt = {
|
|
type = 'node',
|
|
above = {
|
|
x = random_pos.x,
|
|
y = random_pos.y + 1,
|
|
z = random_pos.z
|
|
},
|
|
under = {
|
|
x = random_pos.x,
|
|
y = random_pos.y,
|
|
z = random_pos.z
|
|
}
|
|
}
|
|
|
|
if floats_on_water then
|
|
pt.above.y = random_pos.y
|
|
pt.under.y = random_pos.y - 1
|
|
end
|
|
|
|
returned_itemstack = random_decor_item_def.on_place(ItemStack(random_decor_item), user, pt)
|
|
|
|
if returned_itemstack and returned_itemstack:is_empty() then
|
|
returned_itemstack_success = returned_itemstack_success + 1
|
|
x_farming.x_bonemeal.particle_effect(pt.above)
|
|
end
|
|
elseif random_decor_item_def ~= nil then
|
|
---everything else
|
|
local pos_y = 1
|
|
|
|
if random_decor.place_offset_y ~= nil then
|
|
pos_y = random_decor.place_offset_y
|
|
end
|
|
|
|
x_farming.x_bonemeal.particle_effect(random_pos)
|
|
minetest.set_node({
|
|
x = random_pos.x,
|
|
y = random_pos.y + pos_y,
|
|
z = random_pos.z
|
|
},
|
|
{ name = random_decor_item })
|
|
end
|
|
|
|
table.remove(positions, idx)
|
|
else
|
|
return result
|
|
end
|
|
end
|
|
else
|
|
return result
|
|
end
|
|
|
|
---take item
|
|
if user and returned_itemstack_success > 0
|
|
and not x_farming.x_bonemeal.is_creative(user:get_player_name())
|
|
then
|
|
itemstack:take_item()
|
|
end
|
|
|
|
result.success = true
|
|
result.itemstack = itemstack
|
|
return result
|
|
end
|
|
|
|
---Handle farming and farming addons plants.
|
|
---Needed to copy this function from minetest_game and modify it in order to ommit some checks (e.g. light..)
|
|
---@param itemstack ItemStack
|
|
---@param user ObjectRef | nil
|
|
---@param pointed_thing PointedThingDef
|
|
---@return { ['success']: boolean, ['itemstack']: ItemStack }
|
|
function x_farming.x_bonemeal.grow_farming(itemstack, user, pointed_thing)
|
|
local result = {
|
|
success = false,
|
|
itemstack = itemstack
|
|
}
|
|
local pos_under = pointed_thing.under
|
|
local replace_node_name = minetest.get_node(pos_under).name
|
|
local ndef = minetest.registered_nodes[replace_node_name]
|
|
local take_item = false
|
|
|
|
if not ndef.next_plant
|
|
or ndef.next_plant == 'x_farming:pumpkin_fruit'
|
|
or ndef.next_plant == 'x_farming:melon_fruit'
|
|
then
|
|
return result
|
|
end
|
|
|
|
local pos0 = vector.subtract(pointed_thing.under, 3)
|
|
local pos1 = vector.add(pointed_thing.under, 3)
|
|
local positions = minetest.find_nodes_in_area(pos0, pos1, { 'group:plant', 'group:seed' })
|
|
|
|
for i, pos in ipairs(positions) do
|
|
local isFertile = false
|
|
replace_node_name = minetest.get_node(pos).name
|
|
|
|
---check if on wet soil
|
|
local below = minetest.get_node({ x = pos.x, y = pos.y - 1, z = pos.z })
|
|
local below_def = minetest.registered_nodes[below.name]
|
|
|
|
if minetest.get_item_group(below.name, 'soil') == 3 or below.name == 'mcl_farming:soil_wet' then
|
|
local current_step = tonumber(string.reverse(string.reverse(replace_node_name):split('_')[1]))
|
|
local max_step = farming_steps[replace_node_name:gsub('_%d+', '', 1)]
|
|
|
|
---check if seed
|
|
---farming:seed_wheat
|
|
local mod_plant = replace_node_name:split(':')
|
|
---seed_wheat
|
|
local seed_plant = mod_plant[2]:split('_')
|
|
local seed_name = replace_node_name
|
|
|
|
if seed_plant[1] == 'seed' then
|
|
current_step = 0
|
|
if replace_node_name == 'x_farming:seed_obsidian_wart' then
|
|
replace_node_name = mod_plant[1] .. ':' .. seed_plant[2] .. '_' .. seed_plant[3]
|
|
else
|
|
replace_node_name = mod_plant[1] .. ':' .. seed_plant[2]
|
|
end
|
|
max_step = farming_steps[replace_node_name]
|
|
replace_node_name = replace_node_name .. '_' .. current_step
|
|
else
|
|
if string.find(replace_node_name, 'obsidian_wart') then
|
|
seed_name = mod_plant[1] .. ':seed_' .. seed_plant[1] .. '_' .. seed_plant[2]
|
|
else
|
|
seed_name = mod_plant[1] .. ':seed_' .. seed_plant[1]
|
|
end
|
|
end
|
|
|
|
---search for fertility (again after checking soil)
|
|
local seed_def = minetest.registered_nodes[seed_name]
|
|
|
|
if seed_def and below_def then
|
|
if below_def.groups then
|
|
for _, v in ipairs(seed_def.fertility) do
|
|
if not isFertile then
|
|
isFertile = x_farming.x_bonemeal.groupContains(below_def.groups, v, 1) or below.name == 'mcl_farming:soil_wet'
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if current_step ~= nil and max_step ~= nil and current_step ~= max_step and isFertile then
|
|
local available_steps = max_step - current_step
|
|
local new_step = max_step - available_steps + math.random(available_steps)
|
|
local new_plant = replace_node_name:gsub('_%d+', '_' .. new_step, 1)
|
|
take_item = true
|
|
|
|
local placenode_def = minetest.registered_nodes[new_plant]
|
|
|
|
local placenode = { name = new_plant }
|
|
if placenode_def and placenode_def.place_param2 then
|
|
placenode.param2 = placenode_def.place_param2
|
|
end
|
|
x_farming.x_bonemeal.particle_effect(pos)
|
|
minetest.swap_node(pos, placenode)
|
|
end
|
|
end
|
|
end
|
|
|
|
---take item if not in creative
|
|
if user and not x_farming.x_bonemeal.is_creative(user:get_player_name()) and take_item then
|
|
itemstack:take_item()
|
|
end
|
|
|
|
return {
|
|
success = true,
|
|
itemstack = itemstack
|
|
}
|
|
end
|
|
|
|
---XBonemeal on_use
|
|
---@param self table x_farming.x_bonemeal
|
|
---@param itemstack ItemStack
|
|
---@param user ObjectRef | nil
|
|
---@param pointed_thing any
|
|
---@return { ['success']: boolean, ['itemstack']: ItemStack }
|
|
function x_farming.x_bonemeal.on_use(self, itemstack, user, pointed_thing)
|
|
local result = {
|
|
success = false,
|
|
itemstack = itemstack
|
|
}
|
|
|
|
if not user then
|
|
return result
|
|
end
|
|
|
|
local under = pointed_thing.under
|
|
|
|
if not under then
|
|
return result
|
|
end
|
|
if pointed_thing.type ~= 'node' then
|
|
return result
|
|
end
|
|
if minetest.is_protected(under, user:get_player_name()) then
|
|
return result
|
|
end
|
|
|
|
local node = minetest.get_node(under)
|
|
|
|
if not node then
|
|
return result
|
|
end
|
|
if node.name == 'ignore' then
|
|
return result
|
|
end
|
|
|
|
local mod = node.name:split(':')[1]
|
|
|
|
if (mod == 'farming' or mod == 'x_farming')
|
|
and not string.find(node.name, '_sapling')
|
|
and not string.find(node.name, '_seedling')
|
|
then
|
|
--
|
|
-- Farming
|
|
--
|
|
return self.grow_farming(itemstack, user, pointed_thing)
|
|
elseif self.tree_defs[node.name] then
|
|
--
|
|
-- Default (Trees, Bushes, Papyrus)
|
|
--
|
|
local def = self.tree_defs[node.name]
|
|
local chance = math.random(1, def.chance)
|
|
|
|
if chance == 1 then
|
|
local success = def.grow_tree(under)
|
|
|
|
if not success then
|
|
return result
|
|
end
|
|
|
|
self.particle_effect({ x = under.x, y = under.y + 1, z = under.z })
|
|
end
|
|
|
|
-- take item if not in creative
|
|
if not self.is_creative(user:get_player_name()) then
|
|
itemstack:take_item()
|
|
end
|
|
|
|
return {
|
|
success = true,
|
|
itemstack = itemstack
|
|
}
|
|
else
|
|
return self.grow_grass_and_flowers(itemstack, user, pointed_thing)
|
|
end
|
|
end
|
|
|
|
--- API for registering tree growing from saplings using bonemeal
|
|
function x_farming.x_bonemeal.register_tree_defs(self, defs)
|
|
if not defs or type(defs) ~= 'table' then
|
|
minetest.log('warning', '[x_farming][x_bonemeal] Missing or incorrect definition: \n' .. dump(defs))
|
|
end
|
|
|
|
for _, value in ipairs(defs) do
|
|
local def = table.copy(value)
|
|
if not self.tree_defs[def.name] then
|
|
self.tree_defs[def.name] = value
|
|
end
|
|
end
|
|
end
|
|
|
|
-- TheTermos (MIT)
|
|
-- vec components can be omitted e.g. vec={y=1}
|
|
function x_farming.pos_shift(pos, vec)
|
|
vec.x = vec.x or 0
|
|
vec.y = vec.y or 0
|
|
vec.z = vec.z or 0
|
|
|
|
return {
|
|
x = pos.x + vec.x,
|
|
y = pos.y + vec.y,
|
|
z = pos.z + vec.z
|
|
}
|
|
end
|
|
|
|
-- TheTermos (MIT)
|
|
function x_farming.get_node_pos(pos)
|
|
return {
|
|
x = math.floor(pos.x + 0.5),
|
|
y = math.floor(pos.y + 0.5),
|
|
z = math.floor(pos.z + 0.5),
|
|
}
|
|
end
|
|
|
|
-- TheTermos (MIT)
|
|
function x_farming.nodeatpos(pos)
|
|
local node = minetest.get_node_or_nil(pos)
|
|
if node then
|
|
return minetest.registered_nodes[node.name]
|
|
end
|
|
end
|
|
|
|
-- TheTermos (MIT)
|
|
function x_farming.get_node_height(pos)
|
|
local npos = x_farming.get_node_pos(pos)
|
|
local node = x_farming.nodeatpos(npos)
|
|
if node == nil then
|
|
return nil
|
|
end
|
|
|
|
if node.walkable then
|
|
if node.drawtype == 'nodebox' then
|
|
if node.node_box and node.node_box.type == 'fixed' then
|
|
if type(node.node_box.fixed[1]) == 'number' then
|
|
return npos.y + node.node_box.fixed[5], 0, false
|
|
elseif type(node.node_box.fixed[1]) == 'table' then
|
|
return npos.y + node.node_box.fixed[1][5], 0, false
|
|
else
|
|
-- todo handle table of boxes
|
|
return npos.y + 0.5, 1, false
|
|
end
|
|
elseif node.node_box and node.node_box.type == 'leveled' then
|
|
return minetest.get_node_level(pos) / 64 - 0.5 + x_farming.get_node_pos(pos).y, 0, false
|
|
else
|
|
-- the unforeseen
|
|
return npos.y + 0.5, 1, false
|
|
end
|
|
else
|
|
-- full node
|
|
return npos.y + 0.5, 1, false
|
|
end
|
|
else
|
|
local liquidflag = false
|
|
if node.drawtype == 'liquid' then liquidflag = true end
|
|
return npos.y - 0.5, -1, liquidflag
|
|
end
|
|
end
|
|
|
|
-- TheTermos (MIT)
|
|
-- get_terrain_height
|
|
-- steps(optional) number of recursion steps; default=3
|
|
-- dir(optional) is 1=up, -1=down, 0=both; default=0
|
|
-- liquidflag(forbidden) never provide this parameter.
|
|
-- dir is 1=up, -1=down, 0=both
|
|
function x_farming.get_terrain_height(pos, steps, dir, liquidflag)
|
|
steps = steps or 3
|
|
dir = dir or 0
|
|
|
|
local h, f, l = x_farming.get_node_height(pos)
|
|
if h == nil then
|
|
return nil
|
|
end
|
|
if l then
|
|
liquidflag = true
|
|
end
|
|
|
|
if f == 0 then
|
|
return h, liquidflag
|
|
end
|
|
|
|
if dir == 0 or dir == f then
|
|
steps = steps - 1
|
|
if steps <= 0 then
|
|
return nil
|
|
end
|
|
|
|
return x_farming.get_terrain_height(x_farming.pos_shift(pos, { y = f }), steps, f, liquidflag)
|
|
else
|
|
return h, liquidflag
|
|
end
|
|
end
|
|
|
|
-- TheTermos (MIT)
|
|
-- modified by SaKeL
|
|
function x_farming.get_spawn_pos_abr(dtime, intrvl, radius, chance, reduction)
|
|
dtime = math.min(dtime, 0.1)
|
|
local players = minetest.get_connected_players()
|
|
intrvl = 1 / intrvl
|
|
|
|
if math.random() < dtime * (intrvl * #players) then
|
|
-- choose random player
|
|
local player = players[math.random(#players)]
|
|
local vel = player:get_velocity()
|
|
local spd = vector.length(vel)
|
|
chance = (1 - chance) * 1 / (spd * 0.75 + 1)
|
|
|
|
local yaw
|
|
if spd > 1 then
|
|
-- spawn in the front arc
|
|
yaw = minetest.dir_to_yaw(vel) + math.random() * 0.35 - 0.75
|
|
else
|
|
-- random yaw
|
|
yaw = math.random() * math.pi * 2 - math.pi
|
|
end
|
|
|
|
local pos = player:get_pos()
|
|
local dir = vector.multiply(minetest.yaw_to_dir(yaw), radius)
|
|
local pos2 = vector.add(pos, dir)
|
|
|
|
pos2.y = pos2.y - 5
|
|
|
|
local height, liquidflag = x_farming.get_terrain_height(pos2, 32)
|
|
if height then
|
|
local objs = minetest.find_node_near(pos, radius * 1.1, { 'group:bee' }) or {}
|
|
|
|
-- count mobs in abrange
|
|
for _, obj in ipairs(objs) do
|
|
chance = chance + (1 - chance) * reduction
|
|
end
|
|
|
|
if chance < math.random() then
|
|
pos2.y = height
|
|
objs = minetest.get_objects_inside_radius(pos2, radius * 0.95)
|
|
|
|
-- do not spawn if another player around
|
|
for _, obj in ipairs(objs) do
|
|
if obj:is_player() then
|
|
return
|
|
end
|
|
end
|
|
|
|
return pos2, liquidflag
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function x_farming.on_flood_candle(pos, oldnode, newnode)
|
|
local drops = minetest.get_node_drops(oldnode)
|
|
|
|
for _, item_name in ipairs(drops) do
|
|
minetest.add_item(pos, ItemStack(item_name))
|
|
end
|
|
|
|
-- Play flame-extinguish sound if liquid is not an 'igniter'
|
|
local nodedef = minetest.registered_items[newnode.name]
|
|
|
|
if not (nodedef and nodedef.groups
|
|
and nodedef.groups.igniter and nodedef.groups.igniter > 0)
|
|
and minetest.get_item_group(oldnode.name, 'candle_on') > 0
|
|
then
|
|
minetest.sound_play(
|
|
'x_farming_extinguish_candle',
|
|
{ pos = pos, max_hear_distance = 16, gain = 0.07 },
|
|
true
|
|
)
|
|
end
|
|
|
|
-- Remove the torch node
|
|
return false
|
|
end
|
|
|
|
-- Feasts
|
|
function x_farming.register_feast(name, def)
|
|
local g = {
|
|
-- MTG
|
|
choppy = 3,
|
|
oddly_breakable_by_hand = 3,
|
|
compost = 100,
|
|
no_silktouch = 1,
|
|
-- MCL
|
|
handy = 1,
|
|
shearsy = 1,
|
|
deco_block = 1,
|
|
non_mycelium_plant = 1,
|
|
fire_encouragement = 60,
|
|
fire_flammability = 100,
|
|
dig_by_water = 1,
|
|
destroy_by_lava_flow = 1,
|
|
compostability = 100,
|
|
food = 2,
|
|
eatable = 1,
|
|
-- ALL
|
|
flammable = 2,
|
|
attached_node = 1,
|
|
}
|
|
|
|
-- merge groups from `def`
|
|
if def.groups then
|
|
for group, value in pairs(def.groups) do
|
|
g[group] = value
|
|
end
|
|
end
|
|
|
|
local _def = {
|
|
description = def.description,
|
|
short_description = def.short_description or def.description,
|
|
drawtype = 'mesh',
|
|
mesh = def.mesh,
|
|
use_texture_alpha = def.use_texture_alpha or 'clip',
|
|
inventory_image = def.inventory_image or ('x_farming_' .. name .. '_item.png'),
|
|
wield_image = def.wield_image or ('x_farming_' .. name .. '_item.png'),
|
|
wield_scale = { x = 2, y = 2, z = 1 },
|
|
paramtype = 'light',
|
|
paramtype2 = '4dir',
|
|
is_ground_content = false,
|
|
walkable = false,
|
|
selection_box = def.selection_box,
|
|
groups = g,
|
|
_mcl_blast_resistance = 0,
|
|
_mcl_hardness = 0,
|
|
sounds = def.sounds or x_farming.node_sound_wood_defaults(),
|
|
sunlight_propagates = true,
|
|
}
|
|
|
|
for i = 1, def.steps do
|
|
local d = table.copy(_def)
|
|
|
|
d._next_step = i + 1
|
|
d.tiles = {
|
|
{ name = 'x_farming_' .. name .. '_mesh.png', backface_culling = def.tiles_backface_culling or false },
|
|
{ name = 'x_farming_' .. name .. '_mesh_' .. i .. '.png', backface_culling = def.tiles_backface_culling or false },
|
|
}
|
|
|
|
if i ~= 1 then
|
|
d.groups['not_in_creative_inventory'] = 1
|
|
end
|
|
|
|
-- last (no more food) step
|
|
if i == def.steps then
|
|
d.drop = def.last_drop or 'x_farming:bowl'
|
|
else
|
|
d.drop = {
|
|
max_items = def.steps - i,
|
|
items = {
|
|
{
|
|
rarity = 1,
|
|
items = {
|
|
'x_farming:bowl_' .. name
|
|
}
|
|
},
|
|
{
|
|
rarity = 1,
|
|
items = {
|
|
'x_farming:bowl_' .. name
|
|
}
|
|
},
|
|
{
|
|
rarity = 1,
|
|
items = {
|
|
'x_farming:bowl_' .. name
|
|
}
|
|
},
|
|
{
|
|
rarity = 1,
|
|
items = {
|
|
'x_farming:bowl_' .. name
|
|
}
|
|
},
|
|
}
|
|
}
|
|
end
|
|
|
|
d.on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
|
|
local n = minetest.registered_nodes[node.name]
|
|
local p_name = clicker:get_player_name()
|
|
local inv = clicker:get_inventory()
|
|
local stack_name = itemstack:get_name()
|
|
|
|
if not n then
|
|
return itemstack
|
|
end
|
|
|
|
-- last (no more food) step
|
|
if n._next_step > def.steps then
|
|
minetest.chat_send_player(p_name, S('There is no more food left!'))
|
|
return itemstack
|
|
end
|
|
|
|
if stack_name == 'x_farming:bowl' then
|
|
minetest.swap_node(pos, { name = 'x_farming:' .. name .. '_' .. n._next_step, param2 = node.param2 })
|
|
|
|
if not minetest.is_creative_enabled(p_name) then
|
|
itemstack:take_item()
|
|
end
|
|
|
|
minetest.sound_play('x_farming_wooden_bowl', {
|
|
pos = pos,
|
|
gain = 0.4,
|
|
max_hear_distance = 16
|
|
})
|
|
|
|
local stack_bowl = ItemStack({ name = 'x_farming:bowl_' .. name })
|
|
|
|
if inv and inv:room_for_item('main', stack_bowl) then
|
|
inv:add_item('main', stack_bowl)
|
|
else
|
|
-- drop on the ground
|
|
minetest.add_item(clicker:get_pos(), stack_bowl)
|
|
end
|
|
else
|
|
minetest.chat_send_player(p_name, S('You need to hold empty bowl if you want to take portion of the food!'))
|
|
end
|
|
|
|
return itemstack
|
|
end
|
|
|
|
-- Node
|
|
minetest.register_node('x_farming:' .. name .. '_' .. i, d)
|
|
|
|
-- Craftitem definition
|
|
local craftitem_def = {
|
|
description = def.short_description .. ' ' .. S('Bowl') .. '\n' .. S('Compost chance') .. ': 100%\n'
|
|
.. minetest.colorize(x_farming.colors.brown, S('Hunger') .. ': 8'),
|
|
inventory_image = 'x_farming_bowl_' .. name .. '.png',
|
|
wield_image = 'x_farming_bowl_' .. name .. '.png',
|
|
groups = {
|
|
-- MTG
|
|
-- X Farming
|
|
compost = 100,
|
|
-- MCL
|
|
food = 3,
|
|
eatable = 10,
|
|
compostability = 100,
|
|
},
|
|
-- MCL
|
|
_mcl_saturation = 12.0,
|
|
}
|
|
|
|
if minetest.get_modpath('farming') then
|
|
craftitem_def.on_use = minetest.item_eat(8)
|
|
end
|
|
|
|
if minetest.get_modpath('mcl_farming') then
|
|
craftitem_def.on_place = minetest.item_eat(8)
|
|
craftitem_def.on_secondary_use = minetest.item_eat(8)
|
|
end
|
|
|
|
-- Craftitem
|
|
minetest.register_craftitem('x_farming:bowl_' .. name, craftitem_def)
|
|
end
|
|
end
|
|
|
|
-- Pies
|
|
|
|
function x_farming.register_pie(name, def)
|
|
local g = {
|
|
-- MTG
|
|
choppy = 3,
|
|
oddly_breakable_by_hand = 3,
|
|
compost = 100,
|
|
no_silktouch = 1,
|
|
-- MCL
|
|
handy = 1,
|
|
shearsy = 1,
|
|
deco_block = 1,
|
|
non_mycelium_plant = 1,
|
|
fire_encouragement = 60,
|
|
fire_flammability = 100,
|
|
dig_by_water = 1,
|
|
destroy_by_lava_flow = 1,
|
|
compostability = 100,
|
|
food = 2,
|
|
eatable = 1,
|
|
-- ALL
|
|
flammable = 2,
|
|
attached_node = 1,
|
|
}
|
|
|
|
-- merge groups from `def`
|
|
if def.groups then
|
|
for group, value in pairs(def.groups) do
|
|
g[group] = value
|
|
end
|
|
end
|
|
|
|
local _def = {
|
|
description = def.description,
|
|
short_description = def.short_description or def.description,
|
|
drawtype = 'mesh',
|
|
mesh = def.mesh,
|
|
use_texture_alpha = def.use_texture_alpha or 'clip',
|
|
inventory_image = def.inventory_image or ('x_farming_' .. name .. '_item.png'),
|
|
wield_image = def.wield_image or ('x_farming_' .. name .. '_item.png'),
|
|
wield_scale = { x = 2, y = 2, z = 1 },
|
|
paramtype = 'light',
|
|
paramtype2 = '4dir',
|
|
is_ground_content = false,
|
|
walkable = false,
|
|
selection_box = {
|
|
type = 'fixed',
|
|
fixed = { -7 / 16, -8 / 16, -7 / 16, 7 / 16, -3 / 16, 7 / 16 }
|
|
},
|
|
groups = g,
|
|
_mcl_blast_resistance = 0,
|
|
_mcl_hardness = 0,
|
|
sounds = def.sounds or x_farming.node_sound_wood_defaults(),
|
|
sunlight_propagates = true,
|
|
item_eat = 6
|
|
}
|
|
|
|
for i = 1, def.steps do
|
|
local d = table.copy(_def)
|
|
|
|
d._next_step = i + 1
|
|
d.tiles = {
|
|
{ name = 'x_farming_' .. name .. '_mesh_' .. i .. '.png', backface_culling = def.tiles_backface_culling or false },
|
|
}
|
|
|
|
if i ~= 1 then
|
|
d.groups['not_in_creative_inventory'] = 1
|
|
end
|
|
|
|
d.drop = {
|
|
max_items = def.steps - i + 1,
|
|
items = {
|
|
{
|
|
rarity = 1,
|
|
items = {
|
|
'x_farming:slice_' .. name
|
|
}
|
|
},
|
|
{
|
|
rarity = 1,
|
|
items = {
|
|
'x_farming:slice_' .. name
|
|
}
|
|
},
|
|
{
|
|
rarity = 1,
|
|
items = {
|
|
'x_farming:slice_' .. name
|
|
}
|
|
},
|
|
{
|
|
rarity = 1,
|
|
items = {
|
|
'x_farming:slice_' .. name
|
|
}
|
|
},
|
|
}
|
|
}
|
|
|
|
d.on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
|
|
local n = minetest.registered_nodes[node.name]
|
|
|
|
if not n then
|
|
return itemstack
|
|
end
|
|
|
|
if i < def.steps then
|
|
minetest.swap_node(pos, { name = 'x_farming:' .. name .. '_' .. n._next_step, param2 = node.param2 })
|
|
else
|
|
minetest.remove_node(pos)
|
|
minetest.check_for_falling(pos)
|
|
end
|
|
|
|
local sound_name = 'x_farming_wooden_bowl'
|
|
|
|
if minetest.get_modpath('hunger_ng') then
|
|
hunger_ng.alter_hunger(clicker:get_player_name(), _def.item_eat)
|
|
sound_name = 'hunger_ng_eat'
|
|
elseif minetest.get_modpath('hbhunger') then
|
|
hbhunger.eat(_def.item_eat, nil, ItemStack({ name = 'x_farming:' .. name .. '_1' }), clicker, pointed_thing)
|
|
sound_name = nil
|
|
elseif minetest.get_modpath('stamina') and minetest.global_exists('stamina') then
|
|
-- extra check for global variable since there are some mods called "stamina" without registering global "stamina" namespace
|
|
-- @see https://content.minetest.net/threads/6791/
|
|
stamina.change_saturation(clicker, _def.item_eat)
|
|
sound_name = 'stamina_eat'
|
|
elseif minetest.get_modpath('mcl_hunger') then
|
|
local h = mcl_hunger.get_hunger(clicker)
|
|
mcl_hunger.set_hunger(clicker, math.min(h + _def.item_eat, 20))
|
|
sound_name = 'mcl_hunger_bite'
|
|
else
|
|
minetest.item_eat(_def.item_eat)
|
|
end
|
|
|
|
if sound_name then
|
|
minetest.sound_play(sound_name, { pos = pos, gain = 0.7, max_hear_distance = 5 }, true)
|
|
end
|
|
|
|
return itemstack
|
|
end
|
|
|
|
-- Node
|
|
minetest.register_node('x_farming:' .. name .. '_' .. i, d)
|
|
|
|
local craftitem_def = {
|
|
description = def.short_description .. ' ' .. S('Slice') .. '\n' .. S('Compost chance') .. ': 100%\n'
|
|
.. minetest.colorize(x_farming.colors.brown, S('Hunger') .. ': ' .. _def.item_eat),
|
|
inventory_image = 'x_farming_slice_' .. name .. '.png',
|
|
wield_image = 'x_farming_slice_' .. name .. '.png',
|
|
groups = {
|
|
-- MTG
|
|
-- X Farming
|
|
compost = 100,
|
|
-- MCL
|
|
food = 3,
|
|
eatable = 10,
|
|
compostability = 100,
|
|
},
|
|
-- MCL
|
|
_mcl_saturation = 10.0,
|
|
}
|
|
|
|
if minetest.get_modpath('farming') then
|
|
craftitem_def.on_use = minetest.item_eat(_def.item_eat)
|
|
end
|
|
|
|
if minetest.get_modpath('mcl_farming') then
|
|
craftitem_def.on_place = minetest.item_eat(_def.item_eat)
|
|
craftitem_def.on_secondary_use = minetest.item_eat(_def.item_eat)
|
|
end
|
|
|
|
-- Craftitem
|
|
minetest.register_craftitem('x_farming:slice_' .. name, craftitem_def)
|
|
end
|
|
end
|
|
|
|
--
|
|
-- MCL
|
|
--
|
|
|
|
function x_farming.mcl.create_soil(pos, inv)
|
|
if pos == nil then
|
|
return false
|
|
end
|
|
local node = minetest.get_node(pos)
|
|
local name = node.name
|
|
local above = minetest.get_node({ x = pos.x, y = pos.y + 1, z = pos.z })
|
|
if minetest.get_item_group(name, 'cultivatable') == 2 then
|
|
if above.name == 'air' then
|
|
node.name = 'mcl_farming:soil'
|
|
minetest.set_node(pos, node)
|
|
minetest.sound_play('x_farming_dirt_hit', { pos = pos, gain = 0.5 }, true)
|
|
return true
|
|
end
|
|
elseif minetest.get_item_group(name, 'cultivatable') == 1 then
|
|
if above.name == 'air' then
|
|
node.name = 'mcl_core:dirt'
|
|
minetest.set_node(pos, node)
|
|
minetest.sound_play('x_farming_dirt_hit', { pos = pos, gain = 0.6 }, true)
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
function x_farming.mcl.hoe_on_place_function(uses)
|
|
return function(itemstack, user, pointed_thing)
|
|
-- Call on_rightclick if the pointed node defines it
|
|
local node = minetest.get_node(pointed_thing.under)
|
|
|
|
-- Custom: add support for MTG definition farming
|
|
local regN = minetest.registered_nodes
|
|
if regN[node.name].soil ~= nil
|
|
and regN[node.name].soil.wet ~= nil
|
|
and regN[node.name].soil.dry ~= nil
|
|
then
|
|
return x_farming.hoe_on_use(itemstack, user, pointed_thing, uses)
|
|
end
|
|
|
|
if user and not user:get_player_control().sneak then
|
|
if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then
|
|
return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, user, itemstack) or itemstack
|
|
end
|
|
end
|
|
|
|
if minetest.is_protected(pointed_thing.under, user:get_player_name()) then
|
|
minetest.record_protection_violation(pointed_thing.under, user:get_player_name())
|
|
return itemstack
|
|
end
|
|
|
|
if x_farming.mcl.create_soil(pointed_thing.under, user:get_inventory()) then
|
|
if not minetest.is_creative_enabled(user:get_player_name()) then
|
|
itemstack:add_wear_by_uses(uses)
|
|
end
|
|
return itemstack
|
|
end
|
|
end
|
|
end
|