-- License: CC BY-SA 4.0 with rights-holder: Robert Kiraly (OldCoder). -- -- Note: It's believed that there is no significant and unique code -- or media from other Minetest mods remaining in this Minetest mod. -- =================================================================== -- Features: -- -- Nice high-resolution graphics -- Far more fruit variety than in older Minetest "fruit" mods -- Counts existing entities to prevent over-growth -- "catch_up" bug fix -- "/deletefruit" command -- Automatically removes and aliases competing fruit objects -- Can enable or disable ability to pick fruit before it drops -- CoderApples replace Old Apples -- Generates Christmas ornaments -- Christmas ornaments can be removed and moved -- =================================================================== -- Throughout this module: -- -- "lcname" is the lower-case base name of a fruit type. Base name in -- this context means omitting mod prefix. Example: "apple". -- -- "ucname" is the same thing but with the first letter in upper case. -- Example: "Apple". -- =================================================================== coderfruit = {} local grow_time = 200 local coderfruit_disable_pickable = minetest.setting_getbool ("coderfruit_disable_pickable" ) local enable_default_apple = minetest.setting_getbool ("enable_default_apple" ) local winter_wonderland = minetest.setting_getbool ("winter_wonderland" ) local skydoom_special = minetest.setting_getbool ("skydoom_special" ) if not winter_wonderland then winter_wonderland = false end -- =================================================================== coderfruit.starts_with = function (String, Start) return string.sub (String, 1, string.len (Start)) == Start end -- =================================================================== -- This function returns the number of fruit entities near the posi- -- tion specified by "pos". "mbrange" specifies the radius in 16-node -- mapblocks of the range to check. -- "xmas_only" is an optional boolean flag. If "xmas_only" is speci- -- fied, and true, only "xmas*" entities are counted. -- This routine also uses a global flag named "winter_wonderland". -- If "winter_wonderland" is false, this function deletes "xmas*" ent- -- ities that it sees instead of counting them. -- If "winter_wonderland" is true and this function sees a large num- -- ber of "coderfruit" entities in the area but few or none of the -- "xmas*" type, it deletes the existing entities so as to give the -- "xmas*" type a fair chance to appear. -- =================================================================== local count_fruit_entities = function (pos, mbrange, xmas_only) local num_total = 0 local num_xmas = 0 mbrange = mbrange * 16 local objs = minetest.get_objects_inside_radius (pos, mbrange) local prefix = "coderfruit:entity_" local xmaspre = "coderfruit:entity_xmas" if xmas_only ~= nil and xmas_only then prefix = xmaspre end for n = 1, #objs do if not objs [n]:is_player() then local obj = objs [n] local ent = obj:get_luaentity() if ent and ent.name and winter_wonderland == false and coderfruit.starts_with (ent.name, xmaspre) then obj:remove() ent = nil end if ent and ent.name then if coderfruit.starts_with (ent.name, prefix ) then num_total = num_total + 1 end if winter_wonderland and coderfruit.starts_with (ent.name, xmaspre ) then num_xmas = num_xmas + 1 end end end end if winter_wonderland and num_xmas < 5 and num_total > 20 then for n = 1, #objs do if not objs [n]:is_player() then local obj = objs [n] local ent = obj:get_luaentity() if ent and ent.name and coderfruit.starts_with (name, prefix) then obj:remove() ent = nil end end end end return num_total end -- =================================================================== -- This function registers one type of growing-fruit entity. local make_fruit_entity = function (lcname) local entity_base = { hp_max = -1 , is_visible = true , physical = true , pointable = true , timer = 0 , visual = "sprite" , weight = 5 , automatic_rotate = false , makes_footstep_sound = false , collisionbox = { -0.4, -0.4, -0.4, 0.4, 0.4, 0.4 } , visual_size = { x=1.0, y=1.0 } , textures = { "coderfruit_" .. lcname .. ".png" } , on_punch = function (self, puncher) if coderfruit_disable_pickable then return end self.object:remove() local name = "coderfruit:" .. lcname local inv = puncher:get_inventory() inv:add_item ("main", name) end, on_step = function (self, dtime) if coderfruit.starts_with (lcname, "xmas") then return end local pos = self.object:getpos() self.timer = self.timer + dtime if self.timer > grow_time then self.object:remove() local obj = minetest.env:add_item (pos, "coderfruit:" .. lcname) end end , } return entity_base end -- =================================================================== -- This function registers one growing-fruit type. local register_fruit = function (lcname) local entity = make_fruit_entity (lcname) local obj_name = "coderfruit:entity_" .. lcname minetest.register_entity (obj_name, entity) end -- =================================================================== -- This code registers all of the supported growing-fruit types. register_fruit ("acorn" ) register_fruit ("apple" ) register_fruit ("banana" ) register_fruit ("cedar_cone" ) register_fruit ("cherry" ) register_fruit ("lemon" ) register_fruit ("mango" ) register_fruit ("maple_syrup" ) register_fruit ("orange" ) register_fruit ("pear" ) register_fruit ("pine_cone" ) register_fruit ("pineapple" ) register_fruit ("xmas01" ) register_fruit ("xmas02" ) register_fruit ("xmas03" ) -- =================================================================== local fruit_grow_nodenames = { "default:leaves" , "default:cedar_leaves" , "default:cherry_leaves" , "default:lemon_leaves" , "default:maple_leaves" , "default:oak_leaves" , "default:pine_needles" , } if enable_default_apple ~= 1 and enable_default_apple ~= true then table.insert (fruit_grow_nodenames, "default:big_apple_leaves") end if skydoom_special then table.insert (fruit_grow_nodenames, "default:dirt") end -- =================================================================== coderfruit.fruit_tree_range = ocutil.numset ("fruit_tree_range") or 6 if coderfruit.fruit_tree_range < 2 then coderfruit.fruit_tree_range = 2 end if coderfruit.fruit_tree_range > 10 then coderfruit.fruit_tree_range = 10 end -- =================================================================== -- This ABM adds Christmas ornaments. if winter_wonderland then minetest.register_abm ( { nodenames = fruit_grow_nodenames , neighbors = { "default:air" } , catch_up = false , chance = 30 , -- Chance of trigger is 1.0 / this interval = 30 , -- Operation interval action = function (pos) local trpos = minetest.find_node_near (pos, coderfruit.fruit_tree_range, {"group:tree"}) if trpos == nil then if skydoom_special then local nname = minetest.env:get_node (pos).name if nname ~= "default:dirt" then return end else return end end local num_entities = count_fruit_entities (pos, 2, true) if num_entities > 15 then return end local fruit_pos = { x=pos.x, y=pos.y -1, z=pos.z } if minetest.env:get_node (fruit_pos).name ~= "air" then return end local fruit local fruit_num = math.random (1, 100) if fruit_num >= 1 and fruit_num <= 33 then fruit = "coderfruit:entity_xmas01" elseif fruit_num >= 34 and fruit_num <= 66 then fruit = "coderfruit:entity_xmas02" else fruit = "coderfruit:entity_xmas03" end minetest.env:add_entity (fruit_pos, fruit) end , }) end -- =================================================================== -- This ABM makes fruit grow. minetest.register_abm ( { nodenames = fruit_grow_nodenames , neighbors = { "default:air" } , catch_up = false , chance = 300 , -- Chance of trigger is 1.0 / this interval = 30 , -- Operation interval action = function (pos) local trpos = minetest.find_node_near (pos, coderfruit.fruit_tree_range, {"group:tree"}) if trpos == nil then return end local num_entities = count_fruit_entities (pos, 2) if num_entities > 30 then return end local fruit_pos = { x=pos.x, y=pos.y -1, z=pos.z } if minetest.env:get_node (fruit_pos).name ~= "air" then return end local leafname = minetest.env:get_node (pos).name if leafname == "default:dirt" then return end local fruit local fruit_num = math.random (1, 100) if leafname == "default:big_apple_leaves" then fruit = "coderfruit:entity_apple" elseif leafname == "default:cedar_leaves" then fruit = "coderfruit:entity_cedar_cone" elseif leafname == "default:cherry_leaves" then fruit = "coderfruit:entity_cherry" elseif leafname == "default:lemon_leaves" then fruit = "coderfruit:entity_lemon" elseif leafname == "default:maple_leaves" then fruit = "coderfruit:entity_maple_syrup" elseif leafname == "default:oak_leaves" then fruit = "coderfruit:entity_acorn" elseif leafname == "default:pine_needles" then fruit = "coderfruit:entity_pine_cone" elseif fruit_num >= 1 and fruit_num < 15 then fruit = "coderfruit:entity_apple" elseif fruit_num >= 15 and fruit_num < 30 then fruit = "coderfruit:entity_banana" elseif fruit_num >= 30 and fruit_num < 45 then fruit = "coderfruit:entity_cherry" elseif fruit_num >= 45 and fruit_num < 60 then fruit = "coderfruit:entity_mango" elseif fruit_num >= 60 and fruit_num < 75 then fruit = "coderfruit:entity_orange" elseif fruit_num >= 75 and fruit_num < 90 then fruit = "coderfruit:entity_pear" else fruit = "coderfruit:entity_pineapple" end minetest.env:add_entity (fruit_pos, fruit) end , }) -- =================================================================== -- This function registers one dropped-fruit item. local register_fruit_item = function (lcname, ucname) local nodename = "coderfruit:" .. lcname minetest.register_alias (lcname, nodename) if coderfruit.starts_with (lcname, "xmas") then minetest.register_craftitem (nodename, { description = ucname , groups = {} , inventory_image = "coderfruit_" .. lcname .. ".png" , on_use = function (itemstack, user, pt) local node if user == nil then return end if not pt or pt.type ~= "node" then return end if pt.under == nil then return end local pos = ocutil.clone_table (pt.under) pos.y = pos.y - 1 node = minetest.get_node (pos) if node == nil then return end if node.name ~= "air" then return end local downpos= ocutil.clone_table (pos) downpos.y = downpos.y - 1 node = minetest.get_node (pos) if node == nil then return end if node.name ~= "air" then return end if itemstack:take_item() ~= nil then local ename = "coderfruit:entity_" .. lcname minetest.env:add_entity (pos, ename) end return itemstack end }) else minetest.register_craftitem (nodename, { description = ucname , on_use = minetest.item_eat (1) , groups = { fruit=1, eatable=1, } , inventory_image = "coderfruit_" .. lcname .. ".png" , }) end end -- =================================================================== -- This code registers all supported dropped-fruit items. register_fruit_item ("acorn" , "Acorn" ) register_fruit_item ("apple" , "Apple" ) register_fruit_item ("banana" , "Banana" ) register_fruit_item ("cedar_cone" , "Cedar Cone" ) register_fruit_item ("cherry" , "Cherry" ) register_fruit_item ("lemon" , "Lemon" ) register_fruit_item ("mango" , "Mango" ) register_fruit_item ("maple_syrup" , "Maple Syrup" ) register_fruit_item ("orange" , "Orange" ) register_fruit_item ("pear" , "Pear" ) register_fruit_item ("pine_cone" , "Pine Cone" ) register_fruit_item ("pineapple" , "Pineapple" ) register_fruit_item ("xmas01" , "WW Ornament 01" ) register_fruit_item ("xmas02" , "WW Ornament 02" ) register_fruit_item ("xmas03" , "WW Ornament 02" ) -- =================================================================== -- This is a list of names of old entities superseded by this mod. local old_entities = { "treefruit:Bananna_Entity" , "treefruit:Banana_Entity" , "treefruit:Pear_Entity" , "treefruit:Orange_Entity" , "treefruit:Apple_Entity" , "fruit:Bananna_Entity" , "fruit:Banana_Entity" , "fruit:Pear_Entity" , "fruit:Orange_Entity" , "fruit:Apple_Entity" , } -- =================================================================== -- This code removes old entities that are superseded by this mod. for _, entity_name in ipairs (old_entities) do minetest.register_entity (":"..entity_name, { on_activate = function (self, staticdata) self.object:remove() end , }) end -- =================================================================== -- This code creates aliases for old entities that are superseded by -- this mod. minetest.register_alias ("fruit:apple" , "coderfruit:apple" ) minetest.register_alias ("fruit:banana" , "coderfruit:banana" ) minetest.register_alias ("fruit:bananna" , "coderfruit:banana" ) minetest.register_alias ("fruit:orange" , "coderfruit:orange" ) minetest.register_alias ("fruit:pear" , "coderfruit:pear" ) minetest.register_alias ("treefruit:apple" , "coderfruit:apple" ) minetest.register_alias ("treefruit:banana" , "coderfruit:banana" ) minetest.register_alias ("treefruit:bananna" , "coderfruit:banana" ) minetest.register_alias ("treefruit:orange" , "coderfruit:orange" ) minetest.register_alias ("treefruit:pear" , "coderfruit:pear" ) -- =================================================================== -- This code registers an admin chat command "/deletefruit". The com- -- mand removes nearby fruit. minetest.register_chatcommand ("deletefruit", { description = "delete fruit" , params = "" , privs = { worldedit = true } , func = function (plname, params) local player = minetest.env:get_player_by_name (plname) local pos = player:getpos() local ent = nil local tnob = minetest.get_objects_inside_radius (pos, 75) local nnob = table.getn (tnob) if nnob == 0 then return end for foo, obj in ipairs (tnob) do ent = obj:get_luaentity() if ent ~= nil then local name = ent.name if name == nil then name = "" end if coderfruit.starts_with (name, "coderfruit:") then obj:remove() end end end end }) -- =================================================================== -- End of module.