From bbeb047fc92a490396caaf0a288c7dae4c564f3c Mon Sep 17 00:00:00 2001 From: thunderdog1138 Date: Fri, 17 Jul 2020 12:38:14 +0000 Subject: [PATCH] Upload files to 'mods/farming' --- mods/farming/farming.conf_example | 37 ++ mods/farming/food.lua | 352 +++++++++++++++ mods/farming/grass.lua | 46 ++ mods/farming/hoes.lua | 514 ++++++++++++++++++++++ mods/farming/init.lua | 699 ++++++++++++++++++++++++++++++ 5 files changed, 1648 insertions(+) create mode 100644 mods/farming/farming.conf_example create mode 100644 mods/farming/food.lua create mode 100644 mods/farming/grass.lua create mode 100644 mods/farming/hoes.lua create mode 100644 mods/farming/init.lua diff --git a/mods/farming/farming.conf_example b/mods/farming/farming.conf_example new file mode 100644 index 00000000..68a7bcbf --- /dev/null +++ b/mods/farming/farming.conf_example @@ -0,0 +1,37 @@ + +--[[ + Farming settings can be changed here and kept inside mod folder + even after the mod has been updated, or you can place inside + world folder for map specific settings. +--]] + +-- true to enable crop/food in-game and on mapgen set spawn rarety +farming.carrot = 0.001 +farming.potato = 0.001 +farming.tomato = 0.001 +farming.cucumber = 0.001 +farming.corn = 0.001 +farming.coffee = 0.001 +farming.melon = 0.001 +farming.pumpkin = 0.001 +farming.cocoa = true -- true or false only +farming.raspberry = 0.001 +farming.blueberry = 0.001 +farming.rhubarb = 0.001 +farming.beans = 0.001 +farming.grapes = 0.001 +farming.barley = true -- true or false only +farming.chili = 0.003 +farming.hemp = 0.003 +farming.garlic = 0.001 +farming.onion = 0.001 +farming.pepper = 0.002 +farming.pineapple = 0.001 +farming.peas = 0.001 +farming.beetroot = 0.001 +farming.mint = 0.005 +farming.cabbage = 0.001 +farming.grains = true -- true or false only + +-- default rarety of crops on map (higher number = more crops) +farming.rarety = 0.002 diff --git a/mods/farming/food.lua b/mods/farming/food.lua new file mode 100644 index 00000000..d15a8db9 --- /dev/null +++ b/mods/farming/food.lua @@ -0,0 +1,352 @@ + +local S = farming.intllib + +--= Sugar + +minetest.register_craftitem("farming:sugar", { + description = S("Sugar"), + inventory_image = "farming_sugar.png", + groups = {food_sugar = 1, flammable = 3} +}) + +minetest.register_craft({ + type = "cooking", + cooktime = 3, + output = "farming:sugar 2", + recipe = "default:papyrus" +}) + + +--= Salt + +minetest.register_node("farming:salt", { + description = S("Salt"), + inventory_image = "farming_salt.png", + wield_image = "farming_salt.png", + drawtype = "plantlike", + visual_scale = 0.8, + paramtype = "light", + tiles = {"farming_salt.png"}, + groups = {food_salt = 1, vessel = 1, dig_immediate = 3, + attached_node = 1}, + sounds = default.node_sound_defaults(), + selection_box = { + type = "fixed", + fixed = {-0.25, -0.5, -0.25, 0.25, 0.3, 0.25} + } +}) + +minetest.register_craft({ + type = "cooking", + cooktime = 15, + output = "farming:salt", + recipe = "bucket:bucket_water", + replacements = {{"bucket:bucket_water", "bucket:bucket_empty"}} +}) + +--= Rose Water + +minetest.register_node("farming:rose_water", { + description = S("Rose Water"), + inventory_image = "farming_rose_water.png", + wield_image = "farming_rose_water.png", + drawtype = "plantlike", + visual_scale = 0.8, + paramtype = "light", + tiles = {"farming_rose_water.png"}, + groups = {food_rose_water = 1, vessel = 1, dig_immediate = 3, + attached_node = 1}, + sounds = default.node_sound_defaults(), + selection_box = { + type = "fixed", + fixed = {-0.25, -0.5, -0.25, 0.25, 0.3, 0.25} + } +}) + +minetest.register_craft({ + output = "farming:rose_water", + recipe = { + {"flowers:rose", "flowers:rose", "flowers:rose"}, + {"flowers:rose", "flowers:rose", "flowers:rose"}, + {"bucket:bucket_water", "group:food_pot", "vessels:glass_bottle"} + }, + replacements = { + {"bucket:bucket_water", "bucket:bucket_empty"}, + {"group:food_pot", "farming:pot"} + } +}) + +--= Turkish Delight + +minetest.register_craftitem("farming:turkish_delight", { + description = S("Turkish Delight"), + inventory_image = "farming_turkish_delight.png", + groups = {flammable = 3}, + on_use = minetest.item_eat(2) +}) + +minetest.register_craft({ + output = "farming:turkish_delight 4", + recipe = { + {"group:food_gelatin", "group:food_sugar", "group:food_gelatin"}, + {"group:food_sugar", "group:food_rose_water", "group:food_sugar"}, + {"group:food_cornstarch", "group:food_sugar", "dye:pink"} + }, + replacements = { + {"group:food_cornstarch", "farming:bowl"}, + {"group:food_rose_water", "vessels:glass_bottle"} + } +}) + +--= Garlic Bread + +minetest.register_craftitem("farming:garlic_bread", { + description = S("Garlic Bread"), + inventory_image = "farming_garlic_bread.png", + groups = {flammable = 3}, + on_use = minetest.item_eat(2) +}) + +minetest.register_craft({ + type = "shapeless", + output = "farming:garlic_bread", + recipe = {"group:food_toast", "group:food_garlic_clove", "group:food_garlic_clove"} +}) + +--= Donuts (thanks to Bockwurst for making the donut images) + +minetest.register_craftitem("farming:donut", { + description = S("Donut"), + inventory_image = "farming_donut.png", + on_use = minetest.item_eat(4) +}) + +minetest.register_craft({ + output = "farming:donut 3", + recipe = { + {"", "group:food_wheat", ""}, + {"group:food_wheat", "group:food_sugar", "group:food_wheat"}, + {"", "group:food_wheat", ""} + } +}) + +minetest.register_craftitem("farming:donut_chocolate", { + description = S("Chocolate Donut"), + inventory_image = "farming_donut_chocolate.png", + on_use = minetest.item_eat(6) +}) + +minetest.register_craft({ + output = "farming:donut_chocolate", + recipe = { + {"group:food_cocoa"}, + {"farming:donut"} + } +}) + +minetest.register_craftitem("farming:donut_apple", { + description = S("Apple Donut"), + inventory_image = "farming_donut_apple.png", + on_use = minetest.item_eat(6) +}) + +minetest.register_craft({ + output = "farming:donut_apple", + recipe = { + {"default:apple"}, + {"farming:donut"} + } +}) + +--= Porridge Oats + +minetest.register_craftitem("farming:porridge", { + description = S("Porridge"), + inventory_image = "farming_porridge.png", + on_use = minetest.item_eat(6, "farming:bowl") +}) + +minetest.after(0, function() + + local fluid = "bucket:bucket_water" + local fluid_return = "bucket:bucket_water" + + if minetest.get_modpath("mobs") and mobs and mobs.mod == "redo" then + fluid = "group:food_milk" + fluid_return = "mobs:bucket_milk" + end + + minetest.register_craft({ + type = "shapeless", + output = "farming:porridge", + recipe = { + "group:food_barley", "group:food_barley", "group:food_wheat", + "group:food_wheat", "group:food_bowl", fluid + }, + replacements = {{fluid_return, "bucket:bucket_empty"}} + }) + + minetest.register_craft({ + type = "shapeless", + output = "farming:porridge", + recipe = { + "group:food_oats", "group:food_oats", "group:food_oats", + "group:food_oats", "group:food_bowl", fluid + }, + replacements = {{fluid_return, "bucket:bucket_empty"}} + }) +end) + +--= Jaffa Cake + +minetest.register_craftitem("farming:jaffa_cake", { + description = S("Jaffa Cake"), + inventory_image = "farming_jaffa_cake.png", + on_use = minetest.item_eat(6) +}) + +minetest.register_craft({ + type = "shapeless", + output = "farming:jaffa_cake", + recipe = { + "farming:baking_tray", "group:food_egg", "group:food_sugar", + "group:food_flour", "group:food_cocoa", "group:food_orange", + "group:food_milk" + }, + replacements = { + {"farming:baking_tray", "farming:baking_tray"}, + {"mobs:bucket_milk", "bucket:bucket_empty"} + } +}) + +-- Apple Pie + +minetest.register_craftitem("farming:apple_pie", { + description = S("Apple Pie"), + inventory_image = "farming_apple_pie.png", + on_use = minetest.item_eat(6) +}) + +minetest.register_craft({ + output = "farming:apple_pie", + type = "shapeless", + recipe = { + "group:food_flour", "group:food_sugar", + "group:food_apple", "group:food_baking_tray" + }, + replacements = {{"group:food_baking_tray", "farming:baking_tray"}} +}) + +-- Cactus Juice + +minetest.register_craftitem("farming:cactus_juice", { + description = S("Cactus Juice"), + inventory_image = "farming_cactus_juice.png", + groups = {vessel = 1, drink = 1}, + on_use = function(itemstack, user, pointed_thing) + if user then + if math.random(5) == 1 then + return minetest.do_item_eat(-1, "vessels:drinking_glass", + itemstack, user, pointed_thing) + else + return minetest.do_item_eat(2, "vessels:drinking_glass", + itemstack, user, pointed_thing) + end + end + end +}) + +minetest.register_craft({ + output = "farming:cactus_juice 2", + type = "shapeless", + recipe = { + "vessels:drinking_glass", "vessels:drinking_glass", + "default:cactus", "farming:juicer" + }, + replacements = { + {"group:food_juicer", "farming:juicer"} + } +}) + +-- Pasta + +minetest.register_craftitem("farming:pasta", { + description = S("Pasta"), + inventory_image = "farming_pasta.png", + groups = {food_pasta = 1} +}) + +if minetest.get_modpath("mobs_animal") or minetest.get_modpath("xanadu")then +minetest.register_craft({ + output = "farming:pasta", + type = "shapeless", + recipe = { + "group:food_flour", "group:food_mixing_bowl", + "group:food_butter" + }, + replacements = {{"group:food_mixing_bowl", "farming:mixing_bowl"}} +}) +else +minetest.register_craft({ + output = "farming:pasta", + type = "shapeless", + recipe = { + "group:food_flour", "group:food_mixing_bowl", + "group:food_oil" + }, + replacements = { + {"group:food_mixing_bowl", "farming:mixing_bowl"}, + {"group:food_oil", "vessels:glass_bottle"} + } +}) +end + +-- Spaghetti + +minetest.register_craftitem("farming:spaghetti", { + description = S("Spaghetti"), + inventory_image = "farming_spaghetti.png", + on_use = minetest.item_eat(8) +}) + +minetest.register_craft({ + output = "farming:spaghetti", + type = "shapeless", + recipe = { + "group:food_pasta", "group:food_saucepan", + "group:food_tomato", "group:food_garlic_clove", "group:food_garlic_clove" + }, + replacements = {{"group:food_saucepan", "farming:saucepan"}} +}) + +-- Korean Bibimbap + +minetest.register_craftitem("farming:bibimbap", { + description = S("Bibimbap"), + inventory_image = "farming_bibimbap.png", + on_use = minetest.item_eat(8, "farming:bowl") +}) + +if minetest.get_modpath("mobs_animal") or minetest.get_modpath("xanadu")then +minetest.register_craft({ + output = "farming:bibimbap", + type = "shapeless", + recipe = { + "group:food_skillet", "group:food_bowl", "group:food_egg", "group:food_rice", + "group:food_chicken_raw", "group:food_cabbage", "group:food_carrot", + "group:food_chili_pepper" + }, + replacements = {{"group:food_skillet", "farming:skillet"}} +}) +else +minetest.register_craft({ + output = "farming:bibimbap", + type = "shapeless", + recipe = { + "group:food_skillet", "group:food_bowl", "group:food_mushroom", + "group:food_rice", "group:food_cabbage", "group:food_carrot", + "group:food_mushroom", "group:food_chili_pepper" + }, + replacements = {{"group:food_skillet", "farming:skillet"}} +}) +end diff --git a/mods/farming/grass.lua b/mods/farming/grass.lua new file mode 100644 index 00000000..86e7ac5b --- /dev/null +++ b/mods/farming/grass.lua @@ -0,0 +1,46 @@ + +for i = 4, 5 do + + -- Override default grass and have it drop Wheat and Oat Seeds + + minetest.override_item("default:grass_" .. i, { + drop = { + max_items = 1, + items = { + {items = {"farming:seed_wheat"}, rarity = 5}, + {items = {"farming:seed_oat"},rarity = 5}, + {items = {"default:grass_1"}} + } + } + }) + + -- Override default dry grass and have it drop Barley and Rye Seeds + + if minetest.registered_nodes["default:dry_grass_1"] then + + minetest.override_item("default:dry_grass_" .. i, { + drop = { + max_items = 1, + items = { + {items = {"farming:seed_barley"}, rarity = 5}, + {items = {"farming:seed_rye"},rarity = 5}, + {items = {"default:dry_grass_1"}} + } + } + }) + end + +end + +-- Override default Jungle Grass and have it drop Cotton and Rice Seeds + +minetest.override_item("default:junglegrass", { + drop = { + max_items = 1, + items = { + {items = {"farming:seed_cotton"}, rarity = 8}, + {items = {"farming:seed_rice"},rarity = 8}, + {items = {"default:junglegrass"}} + } + } +}) diff --git a/mods/farming/hoes.lua b/mods/farming/hoes.lua new file mode 100644 index 00000000..21d435b2 --- /dev/null +++ b/mods/farming/hoes.lua @@ -0,0 +1,514 @@ + +local S = farming.intllib +local tr = minetest.get_modpath("toolranks") + +-- Hoe registration function + +farming.register_hoe = function(name, 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 = "unknown_item.png" + end + + if def.max_uses == nil then + def.max_uses = 30 + end + + -- add hoe group + def.groups = def.groups or {} + def.groups.hoe = 1 + + -- Register the tool + minetest.register_tool(name, { + description = def.description, + inventory_image = def.inventory_image, + on_use = function(itemstack, user, pointed_thing) + return farming.hoe_on_use(itemstack, user, pointed_thing, def.max_uses) + end, + groups = def.groups, + sound = {breaks = "default_tool_breaks"} + }) + + -- 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, def.material, ""}, + {"", "group:stick", ""}, + {"", "group:stick", ""} + } + }) + end +end + +-- Turns dirt with group soil=1 into soil + +function farming.hoe_on_use(itemstack, user, pointed_thing, uses) + + local pt = pointed_thing + + -- am I going to hoe the top of a dirt node? + if not pt or pt.type ~= "node" + or pt.above.y ~= pt.under.y + 1 then + return + end + + local under = minetest.get_node(pt.under) + local upos = pointed_thing.under + + if minetest.is_protected(upos, user:get_player_name()) then + minetest.record_protection_violation(upos, user:get_player_name()) + return + end + + 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] + or 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 dirt + if minetest.get_item_group(under.name, "soil") ~= 1 then + return + end + + -- check if (wet) soil defined + local ndef = minetest.registered_nodes[under.name] + if ndef.soil == nil or ndef.soil.wet == nil or ndef.soil.dry == nil then + return + end + + if minetest.is_protected(pt.under, user:get_player_name()) then + minetest.record_protection_violation(pt.under, user:get_player_name()) + return + end + + -- turn the node into soil, wear out item and play sound + minetest.set_node(pt.under, {name = ndef.soil.dry}) + + minetest.sound_play("default_dig_crumbly", {pos = pt.under, gain = 0.5}) + + local wdef = itemstack:get_definition() + local wear = 65535 / (uses - 1) + + if farming.is_creative(user:get_player_name()) then + if tr then + wear = 1 + else + wear = 0 + end + end + + if tr then + itemstack = toolranks.new_afteruse(itemstack, user, under, {wear = wear}) + else + itemstack:add_wear(wear) + end + + 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 + + return itemstack +end + +-- Define Hoes + +farming.register_hoe(":farming:hoe_wood", { + description = S("Wooden Hoe"), + inventory_image = "farming_tool_woodhoe.png", + max_uses = 30, + material = "group:wood" +}) + +minetest.register_craft({ + type = "fuel", + recipe = "farming:hoe_wood", + burntime = 5 +}) + +farming.register_hoe(":farming:hoe_stone", { + description = S("Stone Hoe"), + inventory_image = "farming_tool_stonehoe.png", + max_uses = 90, + material = "group:stone" +}) + +farming.register_hoe(":farming:hoe_steel", { + description = S("Steel Hoe"), + inventory_image = "farming_tool_steelhoe.png", + max_uses = 200, + material = "default:steel_ingot" +}) + +farming.register_hoe(":farming:hoe_bronze", { + description = S("Bronze Hoe"), + inventory_image = "farming_tool_bronzehoe.png", + max_uses = 500, + groups = {not_in_creative_inventory = 1} +}) + +farming.register_hoe(":farming:hoe_mese", { + description = S("Mese Hoe"), + inventory_image = "farming_tool_mesehoe.png", + max_uses = 350, + groups = {not_in_creative_inventory = 1}, +}) + +farming.register_hoe(":farming:hoe_diamond", { + description = S("Diamond Hoe"), + inventory_image = "farming_tool_diamondhoe.png", + max_uses = 500, + groups = {not_in_creative_inventory = 1} +}) + +-- Toolranks support +if tr then + +minetest.override_item("farming:hoe_wood", { + original_description = "Wood Hoe", + description = toolranks.create_description("Wood Hoe")}) + +minetest.override_item("farming:hoe_stone", { + original_description = "Stone Hoe", + description = toolranks.create_description("Stone Hoe")}) + +minetest.override_item("farming:hoe_steel", { + original_description = "Steel Hoe", + description = toolranks.create_description("Steel Hoe")}) + +minetest.override_item("farming:hoe_bronze", { + original_description = "Bronze Hoe", + description = toolranks.create_description("Bronze Hoe")}) + +minetest.override_item("farming:hoe_mese", { + original_description = "Mese Hoe", + description = toolranks.create_description("Mese Hoe")}) + +minetest.override_item("farming:hoe_diamond", { + original_description = "Diamond Hoe", + description = toolranks.create_description("Diamond Hoe")}) +end + + +-- hoe bomb function +local function hoe_area(pos, player) + + -- check for protection + if minetest.is_protected(pos, player:get_player_name()) then + minetest.record_protection_violation(pos, player:get_player_name()) + return + end + + local r = 5 -- radius + + -- remove flora (grass, flowers etc.) + local res = minetest.find_nodes_in_area( + {x = pos.x - r, y = pos.y - 1, z = pos.z - r}, + {x = pos.x + r, y = pos.y + 2, z = pos.z + r}, + {"group:flora"}) + + for n = 1, #res do + minetest.swap_node(res[n], {name = "air"}) + end + + -- replace dirt with tilled soil + res = nil + res = minetest.find_nodes_in_area_under_air( + {x = pos.x - r, y = pos.y - 1, z = pos.z - r}, + {x = pos.x + r, y = pos.y + 2, z = pos.z + r}, + {"group:soil"}) + + for n = 1, #res do + minetest.swap_node(res[n], {name = "farming:soil"}) + end +end + + +-- throwable hoe bomb +minetest.register_entity("farming:hoebomb_entity", { + physical = true, + visual = "sprite", + visual_size = {x = 1.0, y = 1.0}, + textures = {"farming_hoe_bomb.png"}, + collisionbox = {-0.1,-0.1,-0.1,0.1,0.1,0.1}, + lastpos = {}, + player = "", + + on_step = function(self, dtime) + + if not self.player then + + self.object:remove() + + return + end + + local pos = self.object:get_pos() + + if self.lastpos.x ~= nil then + + local vel = self.object:getvelocity() + + -- only when potion hits something physical + if vel.x == 0 + or vel.y == 0 + or vel.z == 0 then + + if self.player ~= "" then + + -- round up coords to fix glitching through doors + self.lastpos = vector.round(self.lastpos) + + hoe_area(self.lastpos, self.player) + end + + self.object:remove() + + return + + end + end + + self.lastpos = pos + end +}) + + +-- actual throwing function +local function throw_potion(itemstack, player) + + local playerpos = player:get_pos() + + local obj = minetest.add_entity({ + x = playerpos.x, + y = playerpos.y + 1.5, + z = playerpos.z + }, "farming:hoebomb_entity") + + local dir = player:get_look_dir() + local velocity = 20 + + obj:setvelocity({ + x = dir.x * velocity, + y = dir.y * velocity, + z = dir.z * velocity + }) + + obj:setacceleration({ + x = dir.x * -3, + y = -9.5, + z = dir.z * -3 + }) + + obj:get_luaentity().player = player +end + + +-- hoe bomb item +minetest.register_craftitem("farming:hoe_bomb", { + description = S("Hoe Bomb (use or throw on grassy areas to hoe land)"), + inventory_image = "farming_hoe_bomb.png", + groups = {flammable = 2, not_in_creative_inventory = 1}, + on_use = function(itemstack, user, pointed_thing) + + if pointed_thing.type == "node" then + hoe_area(pointed_thing.above, user) + else + throw_potion(itemstack, user) + + if not farming.is_creative(user:get_player_name()) then + + itemstack:take_item() + + return itemstack + end + end + end, +}) + +-- Mithril Scythe (special item) + +farming.scythe_not_drops = {"farming:trellis", "farming:beanpole"} + +farming.add_to_scythe_not_drops = function(item) + table.insert(farming.scythe_not_drops, item) +end + +minetest.register_tool("farming:scythe_mithril", { + description = S("Mithril Scythe (Right-click to harvest and replant crops)"), + inventory_image = "farming_scythe_mithril.png", + sound = {breaks = "default_tool_breaks"}, + + on_use = function(itemstack, placer, pointed_thing) + + if pointed_thing.type ~= "node" then + return + end + + local pos = pointed_thing.under + local name = placer:get_player_name() + + if minetest.is_protected(pos, name) then + return + end + + local node = minetest.get_node_or_nil(pos) + + if not node then + return + end + + local def = minetest.registered_nodes[node.name] + + if not def then + return + end + + if not def.drop then + return + end + + if not def.groups + or not def.groups.plant then + return + end + + local drops = minetest.get_node_drops(node.name, "") + + if not drops + or #drops == 0 + or (#drops == 1 and drops[1] == "") then + return + end + + -- get crop name + local mname = node.name:split(":")[1] + local pname = node.name:split(":")[2] + local sname = tonumber(pname:split("_")[2]) + pname = pname:split("_")[1] + + if not sname then + return + end + + -- add dropped items + for _, dropped_item in pairs(drops) do + + -- dont drop items on this list + for _, not_item in pairs(farming.scythe_not_drops) do + + if dropped_item == not_item then + dropped_item = nil + end + end + + if dropped_item then + + local obj = minetest.add_item(pos, dropped_item) + + if obj then + + obj:set_velocity({ + x = math.random(-10, 10) / 9, + y = 3, + z = math.random(-10, 10) / 9 + }) + end + end + end + + -- Run script hook + for _, callback in pairs(core.registered_on_dignodes) do + callback(pos, node, placer) + end + + -- play sound + minetest.sound_play("default_grass_footstep", {pos = pos, gain = 1.0}) + + local replace = mname .. ":" .. pname .. "_1" + + if minetest.registered_nodes[replace] then + + local p2 = minetest.registered_nodes[replace].place_param2 or 1 + + minetest.set_node(pos, {name = replace, param2 = p2}) + else + minetest.set_node(pos, {name = "air"}) + end + + if not farming.is_creative(name) then + + itemstack:add_wear(65535 / 150) -- 150 uses + + return itemstack + end + end, +}) + +if minetest.get_modpath("moreores") then + + minetest.register_craft({ + output = "farming:scythe_mithril", + recipe = { + {"", "moreores:mithril_ingot", "moreores:mithril_ingot"}, + {"moreores:mithril_ingot", "", "group:stick"}, + {"", "", "group:stick"} + } + }) + + farming.register_hoe(":moreores:hoe_silver", { + description = S("%s Hoe"):format(S("Silver")), + inventory_image = "moreores_tool_silverhoe.png", + max_uses = 300, + material = "moreores:silver_ingot" + }) + + farming.register_hoe(":moreores:hoe_mithril", { + description = S("%s Hoe"):format(S("Mithril")), + inventory_image = "moreores_tool_mithrilhoe.png", + max_uses = 1000, + material = "moreores:mithril_ingot" + }) + + -- Toolranks support + if tr then + + local desc = S("%s Hoe"):format(S("Silver")) + + minetest.override_item("moreores:hoe_silver", { + original_description = desc, + description = toolranks.create_description(desc)}) + + desc = S("%s Hoe"):format(S("Mithril")) + + minetest.override_item("moreores:hoe_mithril", { + original_description = desc, + description = toolranks.create_description(desc)}) + end +end diff --git a/mods/farming/init.lua b/mods/farming/init.lua new file mode 100644 index 00000000..68d134cc --- /dev/null +++ b/mods/farming/init.lua @@ -0,0 +1,699 @@ +--[[ + Farming Redo Mod + by TenPlus1 + NEW growing routine by prestidigitator + auto-refill by crabman77 +]] + +farming = { + mod = "redo", + version = "20200702", + path = minetest.get_modpath("farming"), + select = { + type = "fixed", + fixed = {-0.5, -0.5, -0.5, 0.5, -5/16, 0.5} + }, + registered_plants = {} +} + + +local creative_mode_cache = minetest.settings:get_bool("creative_mode") + +function farming.is_creative(name) + return creative_mode_cache or minetest.check_player_privs(name, {creative = true}) +end + + +local statistics = dofile(farming.path .. "/statistics.lua") + +-- Intllib +local S = dofile(farming.path .. "/intllib.lua") +farming.intllib = S + + +-- Utility Function +local time_speed = tonumber(minetest.settings:get("time_speed")) or 72 +local SECS_PER_CYCLE = (time_speed > 0 and (24 * 60 * 60) / time_speed) or 0 +local function clamp(x, min, max) + return (x < min and min) or (x > max and max) or x +end + + +-- return amount of day or night that has elapsed +-- dt is time elapsed, count_day if true counts day, otherwise night +local function day_or_night_time(dt, count_day) + + local t_day = minetest.get_timeofday() + local t1_day = t_day - dt / SECS_PER_CYCLE + local t1_c, t2_c -- t1_c < t2_c and t2_c always in [0, 1) + + if count_day then + + if t_day < 0.25 then + t1_c = t1_day + 0.75 -- Relative to sunup, yesterday + t2_c = t_day + 0.75 + else + t1_c = t1_day - 0.25 -- Relative to sunup, today + t2_c = t_day - 0.25 + end + else + if t_day < 0.75 then + t1_c = t1_day + 0.25 -- Relative to sundown, yesterday + t2_c = t_day + 0.25 + else + t1_c = t1_day - 0.75 -- Relative to sundown, today + t2_c = t_day - 0.75 + end + end + + local dt_c = clamp(t2_c, 0, 0.5) - clamp(t1_c, 0, 0.5) -- this cycle + + if t1_c < -0.5 then + local nc = math.floor(-t1_c) + t1_c = t1_c + nc + dt_c = dt_c + 0.5 * nc + clamp(-t1_c - 0.5, 0, 0.5) + end + + return dt_c * SECS_PER_CYCLE +end + + +-- Growth Logic +local STAGE_LENGTH_AVG = tonumber( + minetest.settings:get("farming_stage_length")) or 200 -- 160 +local STAGE_LENGTH_DEV = STAGE_LENGTH_AVG / 6 + + +-- return plant name and stage from node provided +local function plant_name_stage(node) + + local name + + if type(node) == "table" then + + if node.name then + name = node.name + elseif node.x and node.y and node.z then + node = minetest.get_node_or_nil(node) + name = node and node.name + end + else + name = tostring(node) + end + + if not name or name == "ignore" then + return nil + end + + local sep_pos = name:find("_[^_]+$") + + if sep_pos and sep_pos > 1 then + + local stage = tonumber(name:sub(sep_pos + 1)) + + if stage and stage >= 0 then + return name:sub(1, sep_pos - 1), stage + end + end + + return name, 0 +end + + +-- Map from node name to +-- { plant_name = ..., name = ..., stage = n, stages_left = { node_name, ... } } + +local plant_stages = {} + +farming.plant_stages = plant_stages + +--- Registers the stages of growth of a (possible plant) node. + -- + -- @param node + -- Node or position table, or node name. + -- @return + -- The (possibly zero) number of stages of growth the plant will go through + -- before being fully grown, or nil if not a plant. + +local register_plant_node + +-- Recursive helper +local function reg_plant_stages(plant_name, stage, force_last) + + local node_name = plant_name and plant_name .. "_" .. stage + local node_def = node_name and minetest.registered_nodes[node_name] + + if not node_def then + return nil + end + + local stages = plant_stages[node_name] + + if stages then + return stages + end + + if minetest.get_item_group(node_name, "growing") > 0 then + + local ns = reg_plant_stages(plant_name, stage + 1, true) + local stages_left = (ns and { ns.name, unpack(ns.stages_left) }) or {} + + stages = { + plant_name = plant_name, + name = node_name, + stage = stage, + stages_left = stages_left + } + + if #stages_left > 0 then + + local old_constr = node_def.on_construct + local old_destr = node_def.on_destruct + + minetest.override_item(node_name, + { + on_construct = function(pos) + + if old_constr then + old_constr(pos) + end + + farming.handle_growth(pos) + end, + + on_destruct = function(pos) + + minetest.get_node_timer(pos):stop() + + if old_destr then + old_destr(pos) + end + end, + + on_timer = function(pos, elapsed) + return farming.plant_growth_timer(pos, elapsed, node_name) + end, + }) + end + + elseif force_last then + + stages = { + plant_name = plant_name, + name = node_name, + stage = stage, + stages_left = {} + } + else + return nil + end + + plant_stages[node_name] = stages + + return stages +end + + +local register_plant_node = function(node) + + local plant_name, stage = plant_name_stage(node) + + if plant_name then + + local stages = reg_plant_stages(plant_name, stage, false) + return stages and #stages.stages_left + else + return nil + end +end + + +local function set_growing(pos, stages_left) + + if not stages_left then + return + end + + local timer = minetest.get_node_timer(pos) + + if stages_left > 0 then + + if not timer:is_started() then + + local stage_length = statistics.normal(STAGE_LENGTH_AVG, STAGE_LENGTH_DEV) + + stage_length = clamp(stage_length, 0.5 * STAGE_LENGTH_AVG, 3.0 * STAGE_LENGTH_AVG) + + timer:set(stage_length, -0.5 * math.random() * STAGE_LENGTH_AVG) + end + + elseif timer:is_started() then + timer:stop() + end +end + + +-- detects a crop at given position, starting or stopping growth timer when needed +function farming.handle_growth(pos, node) + + if not pos then + return + end + + local stages_left = register_plant_node(node or pos) + + if stages_left then + set_growing(pos, stages_left) + end +end + + +minetest.after(0, function() + + for _, node_def in pairs(minetest.registered_nodes) do + register_plant_node(node_def) + end +end) + + +-- Just in case a growing type or added node is missed (also catches existing +-- nodes added to map before timers were incorporated). +minetest.register_abm({ + nodenames = {"group:growing"}, + interval = 300, + chance = 1, + catch_up = false, + action = function(pos, node) + farming.handle_growth(pos, node) + end +}) + + +-- Plant timer function that grows plants under the right conditions. +function farming.plant_growth_timer(pos, elapsed, node_name) + + local stages = plant_stages[node_name] + + if not stages then + return false + end + + local max_growth = #stages.stages_left + + if max_growth <= 0 then + return false + end + + -- custom growth check + local chk = minetest.registered_nodes[node_name].growth_check + + if chk then + + if chk(pos, node_name) then + return true + end + + -- otherwise check for wet soil beneath crop + else + local under = minetest.get_node({x = pos.x, y = pos.y - 1, z = pos.z}) + + if minetest.get_item_group(under.name, "soil") < 3 then + return true + end + end + + local growth + local light_pos = {x = pos.x, y = pos.y, z = pos.z} + local lambda = elapsed / STAGE_LENGTH_AVG + + if lambda < 0.1 then + return true + end + + local MIN_LIGHT = minetest.registered_nodes[node_name].minlight or 12 + local MAX_LIGHT = minetest.registered_nodes[node_name].maxlight or 15 + --print ("---", MIN_LIGHT, MAX_LIGHT) + + if max_growth == 1 or lambda < 2.0 then + + local light = (minetest.get_node_light(light_pos) or 0) + --print ("light level:", light) + + if light < MIN_LIGHT or light > MAX_LIGHT then + return true + end + + growth = 1 + else + local night_light = (minetest.get_node_light(light_pos, 0) or 0) + local day_light = (minetest.get_node_light(light_pos, 0.5) or 0) + local night_growth = night_light >= MIN_LIGHT and night_light <= MAX_LIGHT + local day_growth = day_light >= MIN_LIGHT and day_light <= MAX_LIGHT + + if not night_growth then + + if not day_growth then + return true + end + + lambda = day_or_night_time(elapsed, true) / STAGE_LENGTH_AVG + + elseif not day_growth then + + lambda = day_or_night_time(elapsed, false) / STAGE_LENGTH_AVG + end + + growth = statistics.poisson(lambda, max_growth) + + if growth < 1 then + return true + end + end + + if minetest.registered_nodes[stages.stages_left[growth]] then + + local p2 = minetest.registered_nodes[stages.stages_left[growth] ].place_param2 or 1 + + minetest.swap_node(pos, {name = stages.stages_left[growth], param2 = p2}) + else + return true + end + + return growth ~= max_growth +end + + +-- refill placed plant by crabman (26/08/2015) updated by TenPlus1 +function farming.refill_plant(player, plantname, index) + + local inv = player:get_inventory() + local old_stack = inv:get_stack("main", index) + + if old_stack:get_name() ~= "" then + return + end + + for i, stack in ipairs(inv:get_list("main")) do + + if stack:get_name() == plantname and i ~= index then + + inv:set_stack("main", index, stack) + stack:clear() + inv:set_stack("main", i, stack) + + return + end + end +end + + +-- Place Seeds on Soil +function farming.place_seed(itemstack, placer, pointed_thing, plantname) + + local pt = pointed_thing + + -- check if pointing at a node + if not pt or pt.type ~= "node" then + return + end + + local under = minetest.get_node(pt.under) + + -- am I right-clicking on something that has a custom on_place set? + -- thanks to Krock for helping with this issue :) + local def = minetest.registered_nodes[under.name] + if placer and itemstack and def and def.on_rightclick then + return def.on_rightclick(pt.under, under, placer, itemstack) + end + + local above = minetest.get_node(pt.above) + + -- check if pointing at the top of the node + if pt.above.y ~= pt.under.y + 1 then + return + end + + -- return if any of the nodes is not registered + if not minetest.registered_nodes[under.name] + or not minetest.registered_nodes[above.name] then + return + end + + -- can I replace above node, and am I pointing at soil + if not minetest.registered_nodes[above.name].buildable_to + or minetest.get_item_group(under.name, "soil") < 2 + -- avoid multiple seed placement bug + or minetest.get_item_group(above.name, "plant") ~= 0 then + return + end + + -- is player planting seed? + local name = placer and placer:get_player_name() or "" + + -- if not protected then add node and remove 1 item from the itemstack + if not minetest.is_protected(pt.above, name) then + + local p2 = minetest.registered_nodes[plantname].place_param2 or 1 + + minetest.set_node(pt.above, {name = plantname, param2 = p2}) + +--minetest.get_node_timer(pt.above):start(1) +--farming.handle_growth(pt.above)--, node) + + minetest.sound_play("default_place_node", {pos = pt.above, gain = 1.0}) + + if placer and itemstack + and not farming.is_creative(placer:get_player_name()) then + + local name = itemstack:get_name() + + itemstack:take_item() + + -- check for refill + if itemstack:get_count() == 0 then + + minetest.after(0.10, + farming.refill_plant, + placer, + name, + placer:get_wield_index() + ) + end + end + + return itemstack + end +end + + +-- Function to register plants (default farming compatibility) +farming.register_plant = function(name, def) + + if not def.steps then + return nil + end + + local mname = name:split(":")[1] + local pname = name:split(":")[2] + + -- Check def + def.description = def.description or S("Seed") + def.inventory_image = def.inventory_image or "unknown_item.png" + def.minlight = def.minlight or 12 + def.maxlight = def.maxlight or 15 + + -- Register seed + minetest.register_node(":" .. mname .. ":seed_" .. pname, { + + description = def.description, + tiles = {def.inventory_image}, + inventory_image = def.inventory_image, + wield_image = def.inventory_image, + drawtype = "signlike", + groups = {seed = 1, snappy = 3, attached_node = 1, flammable = 2}, + paramtype = "light", + paramtype2 = "wallmounted", + walkable = false, + sunlight_propagates = true, + selection_box = farming.select, + place_param2 = def.place_param2 or nil, + next_plant = mname .. ":" .. pname .. "_1", + + on_place = function(itemstack, placer, pointed_thing) + return farming.place_seed(itemstack, placer, + pointed_thing, mname .. ":" .. pname .. "_1") + end, + }) + + -- Register harvest + minetest.register_craftitem(":" .. mname .. ":" .. pname, { + description = pname:gsub("^%l", string.upper), + 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 g = { + snappy = 3, flammable = 2, plant = 1, growing = 1, + attached_node = 1, not_in_creative_inventory = 1, + } + + -- Last step doesn't need growing=1 so Abm never has to check these + if i == def.steps then + g.growing = 0 + end + + local node_name = mname .. ":" .. pname .. "_" .. i + + local next_plant = nil + + if i < def.steps then + next_plant = mname .. ":" .. pname .. "_" .. (i + 1) + end + + minetest.register_node(node_name, { + drawtype = "plantlike", + waving = 1, + tiles = {mname .. "_" .. pname .. "_" .. i .. ".png"}, + paramtype = "light", + paramtype2 = def.paramtype2, + place_param2 = def.place_param2, + walkable = false, + buildable_to = true, + sunlight_propagates = true, + drop = drop, + selection_box = farming.select, + groups = g, + sounds = default.node_sound_leaves_defaults(), + minlight = def.minlight, + maxlight = def.maxlight, + next_plant = next_plant + }) + end + +-- add to farming.registered_plants +farming.registered_plants[mname .. ":" .. pname] = { + crop = mname .. ":" .. pname, + seed = mname .. ":seed_" .. pname, + steps = def.steps, + minlight = def.minlight, + maxlight = def.maxlight +} +--print(dump(farming.registered_plants[mname .. ":" .. pname])) + -- Return info + return {seed = mname .. ":seed_" .. pname, harvest = mname .. ":" .. pname} +end + + +-- default settings +farming.carrot = 0.001 +farming.potato = 0.001 +farming.tomato = 0.001 +farming.cucumber = 0.001 +farming.corn = 0.001 +farming.coffee = 0.001 +farming.melon = 0.001 +farming.pumpkin = 0.001 +farming.cocoa = true +farming.raspberry = 0.001 +farming.blueberry = 0.001 +farming.rhubarb = 0.001 +farming.beans = 0.001 +farming.grapes = 0.001 +farming.barley = true +farming.chili = 0.003 +farming.hemp = 0.003 +farming.garlic = 0.001 +farming.onion = 0.001 +farming.pepper = 0.002 +farming.pineapple = 0.001 +farming.peas = 0.001 +farming.beetroot = 0.001 +farming.mint = 0.005 +farming.cabbage = 0.001 +farming.grains = true +farming.rarety = 0.002 + + +-- Load new global settings if found inside mod folder +local input = io.open(farming.path.."/farming.conf", "r") +if input then + dofile(farming.path .. "/farming.conf") + input:close() +end + +-- load new world-specific settings if found inside world folder +local worldpath = minetest.get_worldpath() +input = io.open(worldpath.."/farming.conf", "r") +if input then + dofile(worldpath .. "/farming.conf") + input:close() +end + + +-- important items +dofile(farming.path.."/soil.lua") +dofile(farming.path.."/hoes.lua") +dofile(farming.path.."/grass.lua") +dofile(farming.path.."/utensils.lua") + +-- default crops +dofile(farming.path.."/crops/wheat.lua") +dofile(farming.path.."/crops/cotton.lua") + + +-- helper function +local function ddoo(file, check) + + if check then + dofile(farming.path .. "/crops/" .. file) + end +end + +-- add additional crops and food (if enabled) +ddoo("carrot.lua", farming.carrot) +ddoo("potato.lua", farming.potato) +ddoo("tomato.lua", farming.tomato) +ddoo("cucumber.lua", farming.cucumber) +ddoo("corn.lua", farming.corn) +ddoo("coffee.lua", farming.coffee) +ddoo("melon.lua", farming.melon) +ddoo("pumpkin.lua", farming.pumpkin) +ddoo("cocoa.lua", farming.cocoa) +ddoo("raspberry.lua", farming.raspberry) +ddoo("blueberry.lua", farming.blueberry) +ddoo("rhubarb.lua", farming.rhubarb) +ddoo("beans.lua", farming.beans) +ddoo("grapes.lua", farming.grapes) +ddoo("barley.lua", farming.barley) +ddoo("hemp.lua", farming.hemp) +ddoo("garlic.lua", farming.garlic) +ddoo("onion.lua", farming.onion) +ddoo("pepper.lua", farming.pepper) +ddoo("pineapple.lua", farming.pineapple) +ddoo("peas.lua", farming.peas) +ddoo("beetroot.lua", farming.beetroot) +ddoo("chili.lua", farming.chili) +ddoo("ryeoatrice.lua", farming.grains) +ddoo("mint.lua", farming.mint) +ddoo("cabbage.lua", farming.cabbage) + +dofile(farming.path .. "/food.lua") +dofile(farming.path .. "/mapgen.lua") +dofile(farming.path .. "/compatibility.lua") -- Farming Plus compatibility +dofile(farming.path .. "/lucky_block.lua")