From 4e8db84635c73878aa696ffe9526285741f5f0dc Mon Sep 17 00:00:00 2001 From: HybridDog Date: Mon, 23 May 2016 13:10:16 +0200 Subject: [PATCH] Cleanup farming code and small additional changes Some additional changes: Hoes only work if the node is pointed to from above. Hoe description, inventory_image and max_uses must be specified (when registering). Nodes with the field group must have all soil values in the node definition. Field nodes do not loose their param2 value (e.g. rotation) when they dry, get wet or get hoed. The node above the hoed one is not checked for protection because that node is not changed; similarly, the node under a seed is not tested for protection. Document the fertility test for nil player in the hoe on_use function properly test for the creative mod ensure that the on_place function returns an itemstack change missing soil fields message --- game_api.txt | 67 ++++++++----- mods/farming/api.lua | 220 ++++++++++++++++------------------------- mods/farming/mod.conf | 2 +- mods/farming/nodes.lua | 128 ++++++++++++++---------- 4 files changed, 204 insertions(+), 213 deletions(-) diff --git a/game_api.txt b/game_api.txt index bddf7e27..652c2696 100644 --- a/game_api.txt +++ b/game_api.txt @@ -165,11 +165,11 @@ Doors API The doors mod allows modders to register custom doors and trapdoors. -`doors.registered_doors[name] = Door definition` - * Table of registered doors, indexed by door name +`doors.registered_doors[name] = Door definition` + * Table of registered doors, indexed by door name -`doors.registered_trapdoors[name] = Trapdoor definition` - * Table of registered trap doors, indexed by trap door name +`doors.registered_trapdoors[name] = Trapdoor definition` + * Table of registered trap doors, indexed by trap door name `doors.register_door(name, def)` @@ -212,7 +212,7 @@ The doors mod allows modders to register custom doors and trapdoors. * `pos` Position of the door * `node` Node definition * `clicker` Player definition for the player that clicked on the door - + ### Door definition description = "Door description", @@ -226,7 +226,7 @@ The doors mod allows modders to register custom doors and trapdoors. sound_open = sound play for open door, -- optional sound_close = sound play for close door, -- optional protected = false, -- If true, only placer can open the door (locked for others) - on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) + on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) -- optional function containing the on_rightclick callback, defaults to a doors.door_toggle-wrapper ### Trapdoor definition @@ -245,9 +245,9 @@ The doors mod allows modders to register custom doors and trapdoors. sound_open = sound play for open door, -- optional sound_close = sound play for close door, -- optional protected = false, -- If true, only placer can open the door (locked for others) - on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) + on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) -- function containing the on_rightclick callback - on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) + on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) -- function containing the on_rightclick callback ### Fence gate definition @@ -258,7 +258,7 @@ The doors mod allows modders to register custom doors and trapdoors. material = "default:wood", groups = {choppy = 2, oddly_breakable_by_hand = 2, flammable = 2}, sounds = default.node_sound_wood_defaults(), -- optional - on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) + on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) -- function containing the on_rightclick callback @@ -347,13 +347,21 @@ The farming API allows you to easily register plants and hoes. ### Hoe Definition - { - description = "", -- Description for tooltip - inventory_image = "unknown_item.png", -- Image to be used as wield- and inventory image - max_uses = 30, -- Uses until destroyed - material = "", -- Material for recipes - recipe = { -- Craft recipe, if material isn't used + -- Description for tooltip + description = "My Hoe", + + -- Image to be used as wield- and inventory image + inventory_image = "unknown_item.png", + + -- Uses until destroyed + max_uses = 30, + + -- Material for recipes + material = "", + + -- Craft recipe, if material isn't used + recipe = { {"air", "air", "air"}, {"", "group:stick"}, {"", "group:stick"}, @@ -363,14 +371,27 @@ The farming API allows you to easily register plants and hoes. ### Plant definition { - description = "", -- Description of seed item - harvest_description = "", -- Description of harvest item - -- (optional, derived automatically if not provided) - inventory_image = "unknown_item.png", -- Image to be used as seed's wield- and inventory image - steps = 8, -- How many steps the plant has to grow, until it can be harvested - -- ^ Always provide a plant texture for each step, format: modname_plantname_i.png (i = stepnumber) - minlight = 13, -- Minimum light to grow - maxlight = default.LIGHT_MAX -- Maximum light to grow + -- Description of seed item + description = "My Plant", + + -- Description of harvest item (optional, derived automatically if not + -- provided) + harvest_description = "", + + -- Image to be used as seed's wield- and inventory image + inventory_image = "unknown_item.png", + + -- How many steps the plant has to grow, until it can be harvested + steps = 8, + -- ^ Always provide a plant texture for each step, format: + -- modname_plantname_i.png (i = stepnumber) + + -- Groups of soil nodes on which the seed can grow + fertility = {"grassland"}, + + -- Minimum and maximum light to grow + minlight = 1, + maxlight = default.LIGHT_MAX } diff --git a/mods/farming/api.lua b/mods/farming/api.lua index 91d557c7..b4a6b782 100644 --- a/mods/farming/api.lua +++ b/mods/farming/api.lua @@ -1,73 +1,58 @@ -- farming/api.lua --- support for MT game translation. -local S = farming.get_translator - -- Wear out hoes, place soil -- TODO Ignore group:flower farming.registered_plants = {} -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 +function farming.hoe_on_use(itemstack, user, pt, uses) + if not pt or pt.type ~= "node" or pt.above.y ~= pt.under.y + 1 then + -- Only nodes pointed on the top can be hoed 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 + if minetest.get_node(pt.above).name ~= "air" then + -- The hoe is obstructed from moving if there is no free space return end - -- check if the node above the pointed thing is air - if above.name ~= "air" then + local node_under = minetest.get_node(pt.under) + local node_under_def = minetest.registered_nodes[node_under.name] + if not node_under_def or + minetest.get_item_group(node_under.name, "soil") ~= 1 then + -- Not a soil node return end - -- check if pointing at soil - if minetest.get_item_group(under.name, "soil") ~= 1 then + -- Test if soil properties are defined + local soil = node_under_def.soil + if not soil or not soil.wet or not soil.dry 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 + local playername = user and user:get_player_name() or "" + if minetest.is_protected(pt.under, playername) then + minetest.record_protection_violation(pt.under, playername) 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 - if minetest.is_protected(pt.above, user:get_player_name()) then - minetest.record_protection_violation(pt.above, user:get_player_name()) - return - end - - -- turn the node into soil and play sound - minetest.set_node(pt.under, {name = regN[under.name].soil.dry}) + -- Put the node which should appear after applying the hoe + node_under.name = node_under_def.soil.dry + minetest.swap_node(pt.under, node_under) minetest.sound_play("default_dig_crumbly", { pos = pt.under, gain = 0.5, }, true) - if not (creative and creative.is_enabled_for - and creative.is_enabled_for(user:get_player_name())) then - -- wear tool + if minetest.global_exists("creative") + and creative.is_enabled_for(playername) then + return + end + + -- wear tool + itemstack:add_wear(65535 / (uses - 1)) + if itemstack:is_empty() then local wdef = itemstack:get_definition() - itemstack:add_wear(65535/(uses-1)) - -- tool break sound - if itemstack:get_count() == 0 and wdef.sound and wdef.sound.breaks then + if wdef.sound and wdef.sound.breaks then minetest.sound_play(wdef.sound.breaks, {pos = pt.above, gain = 0.5}, true) end @@ -76,31 +61,29 @@ farming.hoe_on_use = function(itemstack, user, pointed_thing, uses) end -- Register new hoes -farming.register_hoe = function(name, def) +function farming.register_hoe(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 + assert(def.description, "Missing hoe description for " .. name) + assert(def.inventory_image, "Missing inventory image for " .. name) + assert(def.max_uses and def.max_uses > 1, + "max_uses are invalid (hoe " .. name .. ")") + -- 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) + 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({ @@ -129,62 +112,43 @@ local function tick_again(pos) end -- Seed placement -farming.place_seed = function(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 - end - if minetest.is_protected(pt.above, player_name) then - minetest.record_protection_violation(pt.above, player_name) +function farming.place_seed(itemstack, placer, pt, plantname) + if not pt or pt.type ~= "node" or pt.above.y ~= pt.under.y + 1 then + -- Seeds can only be placed on top of a node return 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 + local playername = placer and placer:get_player_name() or "" + if minetest.is_protected(pt.above, playername) then + minetest.record_protection_violation(pt.above, playername) + return end - -- check if pointing at the top of the node - if pt.above.y ~= pt.under.y+1 then - return itemstack + local node_above_def = minetest.registered_nodes[ + minetest.get_node(pt.above).name] + if not node_above_def or not node_above_def.buildable_to then + -- We cannot put the seed here + return end - -- check if you can replace the node above the pointed node - if not minetest.registered_nodes[above.name].buildable_to then - return itemstack + -- The seed must be placed onto a soil node + local node_under = minetest.get_node(pt.under) + if minetest.get_item_group(node_under.name, "soil") < 2 then + return 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 - minetest.log("action", player_name .. " places node " .. plantname .. " at " .. - minetest.pos_to_string(pt.above)) + -- Put the seed node + minetest.log("action", playername .. " places node " .. plantname .. + " at " .. minetest.pos_to_string(pt.above)) minetest.add_node(pt.above, {name = plantname, param2 = 1}) tick(pt.above) - if not (creative and creative.is_enabled_for - and creative.is_enabled_for(player_name)) then - itemstack:take_item() + + if minetest.global_exists("creative") + and creative.is_enabled_for(playername) then + return end + + itemstack:take_item() return itemstack end @@ -200,14 +164,15 @@ farming.grow_plant = function(pos, elapsed) -- 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}) + local soil_node = minetest.get_node_or_nil( + {x = pos.x, y = pos.y - 1, z = pos.z}) if not soil_node then 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 then + for _, groupname in ipairs(def.fertility) do + if minetest.get_item_group(soil_node.name, groupname) ~= 0 then local placenode = {name = def.next_plant} if def.place_param2 then placenode.param2 = def.place_param2 @@ -252,48 +217,36 @@ farming.grow_plant = function(pos, elapsed) end -- Register plants -farming.register_plant = function(name, def) - local mname = name:split(":")[1] - local pname = name:split(":")[2] +function farming.register_plant(name, def) + local mname, pname = unpack(name:split(":")) -- 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 + assert(def.description, "Missing description for " .. name) + assert(def.inventory_image, "Missing inventory_image for " .. name) + assert(def.steps, "Missing number of steps for " .. name) + assert(def.fertility and def.fertility[1], "Missing fertility for " .. name) + def.minlight = def.minlight or 1 + def.maxlight = def.maxlight or default.LIGHT_MAX farming.registered_plants[pname] = def -- Register seed local lbm_nodes = {mname .. ":seed_" .. pname} - local g = {seed = 1, snappy = 3, attached_node = 1, flammable = 2} - for k, v in pairs(def.fertility) do - g[v] = 1 + local seed_groups = {seed = 1, snappy = 3, attached_node = 1, flammable = 2} + for _, groupname in ipairs(def.fertility) do + seed_groups[groupname] = 1 end + 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 = g, + groups = seed_groups, paramtype = "light", paramtype2 = "wallmounted", place_param2 = def.place_param2 or nil, -- this isn't actually used for placement @@ -321,7 +274,8 @@ farming.register_plant = function(name, def) pointed_thing) or itemstack end - return farming.place_seed(itemstack, placer, pointed_thing, mname .. ":seed_" .. pname) + return farming.place_seed(itemstack, placer, pointed_thing, + mname .. ":seed_" .. pname) or itemstack end, next_plant = mname .. ":" .. pname .. "_1", on_timer = farming.grow_plant, @@ -350,8 +304,6 @@ farming.register_plant = function(name, def) {items = {mname .. ":seed_" .. pname}, rarity = base_rarity * 2}, } } - local nodegroups = {snappy = 3, flammable = 2, plant = 1, not_in_creative_inventory = 1, attached_node = 1} - nodegroups[pname] = i local next_plant = nil @@ -374,7 +326,8 @@ farming.register_plant = function(name, def) type = "fixed", fixed = {-0.5, -0.5, -0.5, 0.5, -5/16, 0.5}, }, - groups = nodegroups, + groups = {snappy = 3, flammable = 2, plant = 1, + not_in_creative_inventory = 1, attached_node = 1, [pname] = i}, sounds = default.node_sound_leaves_defaults(), next_plant = next_plant, on_timer = farming.grow_plant, @@ -387,15 +340,12 @@ farming.register_plant = function(name, def) minetest.register_lbm({ name = ":" .. mname .. ":start_nodetimer_" .. pname, nodenames = lbm_nodes, - action = function(pos, node) - tick_again(pos) - end, + action = tick_again, }) -- Return - local r = { + return { seed = mname .. ":seed_" .. pname, harvest = mname .. ":" .. pname } - return r end diff --git a/mods/farming/mod.conf b/mods/farming/mod.conf index 9a76a6a7..f60df82e 100644 --- a/mods/farming/mod.conf +++ b/mods/farming/mod.conf @@ -1,4 +1,4 @@ name = farming description = Minetest Game mod: farming depends = default, wool, stairs -optional_depends = dungeon_loot +optional_depends = dungeon_loot, creative diff --git a/mods/farming/nodes.lua b/mods/farming/nodes.lua index b5f90f96..f42e3e3b 100644 --- a/mods/farming/nodes.lua +++ b/mods/farming/nodes.lua @@ -63,7 +63,8 @@ minetest.register_node("farming:soil", { description = S("Soil"), tiles = {"default_dirt.png^farming_soil.png", "default_dirt.png"}, drop = "default:dirt", - groups = {crumbly=3, not_in_creative_inventory=1, soil=2, grassland = 1, field = 1}, + groups = {crumbly = 3, not_in_creative_inventory = 1, soil = 2, + grassland = 1, field = 1}, sounds = default.node_sound_dirt_defaults(), soil = { base = "default:dirt", @@ -74,9 +75,11 @@ minetest.register_node("farming:soil", { minetest.register_node("farming:soil_wet", { description = S("Wet Soil"), - tiles = {"default_dirt.png^farming_soil_wet.png", "default_dirt.png^farming_soil_wet_side.png"}, + tiles = {"default_dirt.png^farming_soil_wet.png", + "default_dirt.png^farming_soil_wet_side.png"}, drop = "default:dirt", - groups = {crumbly=3, not_in_creative_inventory=1, soil=3, wet = 1, grassland = 1, field = 1}, + groups = {crumbly = 3, not_in_creative_inventory = 1, soil = 3, wet = 1, + grassland = 1, field = 1}, sounds = default.node_sound_dirt_defaults(), soil = { base = "default:dirt", @@ -89,7 +92,8 @@ minetest.register_node("farming:dry_soil", { description = S("Savanna Soil"), tiles = {"default_dry_dirt.png^farming_soil.png", "default_dry_dirt.png"}, drop = "default:dry_dirt", - groups = {crumbly=3, not_in_creative_inventory=1, soil=2, grassland = 1, field = 1}, + groups = {crumbly=3, not_in_creative_inventory=1, soil=2, grassland = 1, + field = 1}, sounds = default.node_sound_dirt_defaults(), soil = { base = "default:dry_dirt", @@ -100,9 +104,11 @@ minetest.register_node("farming:dry_soil", { minetest.register_node("farming:dry_soil_wet", { description = S("Wet Savanna Soil"), - tiles = {"default_dry_dirt.png^farming_soil_wet.png", "default_dry_dirt.png^farming_soil_wet_side.png"}, + tiles = {"default_dry_dirt.png^farming_soil_wet.png", + "default_dry_dirt.png^farming_soil_wet_side.png"}, drop = "default:dry_dirt", - groups = {crumbly=3, not_in_creative_inventory=1, soil=3, wet = 1, grassland = 1, field = 1}, + groups = {crumbly=3, not_in_creative_inventory=1, soil=3, wet = 1, + grassland = 1, field = 1}, sounds = default.node_sound_dirt_defaults(), soil = { base = "default:dry_dirt", @@ -123,7 +129,8 @@ minetest.register_node("farming:desert_sand_soil", { description = S("Desert Sand Soil"), drop = "default:desert_sand", tiles = {"farming_desert_sand_soil.png", "default_desert_sand.png"}, - groups = {crumbly=3, not_in_creative_inventory = 1, falling_node=1, sand=1, soil = 2, desert = 1, field = 1}, + groups = {crumbly = 3, not_in_creative_inventory = 1, falling_node = 1, + sand = 1, soil = 2, desert = 1, field = 1}, sounds = default.node_sound_sand_defaults(), soil = { base = "default:desert_sand", @@ -135,8 +142,10 @@ minetest.register_node("farming:desert_sand_soil", { minetest.register_node("farming:desert_sand_soil_wet", { description = S("Wet Desert Sand Soil"), drop = "default:desert_sand", - tiles = {"farming_desert_sand_soil_wet.png", "farming_desert_sand_soil_wet_side.png"}, - groups = {crumbly=3, falling_node=1, sand=1, not_in_creative_inventory=1, soil=3, wet = 1, desert = 1, field = 1}, + tiles = {"farming_desert_sand_soil_wet.png", + "farming_desert_sand_soil_wet_side.png"}, + groups = {crumbly = 3, falling_node = 1, sand = 1, desert = 1, + not_in_creative_inventory = 1, soil = 3, wet = 1, field = 1}, sounds = default.node_sound_sand_defaults(), soil = { base = "default:desert_sand", @@ -182,49 +191,56 @@ minetest.register_abm({ interval = 15, chance = 4, action = function(pos, node) - local n_def = minetest.registered_nodes[node.name] or nil - local wet = n_def.soil.wet or nil - local base = n_def.soil.base or nil - local dry = n_def.soil.dry or nil - if not n_def or not n_def.soil or not wet or not base or not dry then - return - end - pos.y = pos.y + 1 local nn = minetest.get_node_or_nil(pos) - if not nn or not nn.name then + if not nn then return end - local nn_def = minetest.registered_nodes[nn.name] or nil + local nn_def = minetest.registered_nodes[nn.name] pos.y = pos.y - 1 - if nn_def and nn_def.walkable and minetest.get_item_group(nn.name, "plant") == 0 then - minetest.set_node(pos, {name = base}) + local soil = minetest.registered_nodes[node.name].soil + assert(soil and soil.wet and soil.base and soil.dry, + "Field node " .. node.name .. " lacks of either 'wet', 'base' " .. + "or 'dry' properties.") + + if nn_def and nn_def.walkable and + minetest.get_item_group(nn.name, "plant") == 0 then + node.name = soil.base + minetest.set_node(pos, node) return end - -- check if there is water nearby - local wet_lvl = minetest.get_item_group(node.name, "wet") - if minetest.find_node_near(pos, 3, {"group:water"}) then - -- if it is dry soil and not base node, turn it into wet soil - if wet_lvl == 0 then - minetest.set_node(pos, {name = wet}) - end - else - -- only turn back if there are no unloaded blocks (and therefore - -- possible water sources) nearby - if not minetest.find_node_near(pos, 3, {"ignore"}) then - -- turn it back into base if it is already dry - if wet_lvl == 0 then - -- only turn it back if there is no plant/seed on top of it - if minetest.get_item_group(nn.name, "plant") == 0 and minetest.get_item_group(nn.name, "seed") == 0 then - minetest.set_node(pos, {name = base}) - end - -- if its wet turn it back into dry soil - elseif wet_lvl == 1 then - minetest.set_node(pos, {name = dry}) - end + local wet_lvl = minetest.get_item_group(node.name, "wet") + -- Make the node wet if water is near it + if minetest.find_node_near(pos, 3, {"group:water"}) then + -- If it is dry soil and not base node, turn it into wet soil + if wet_lvl == 0 then + node.name = soil.wet + minetest.set_node(pos, node) end + return + end + + -- Only dry out if there are no unloaded blocks (and therefore + -- possible water sources) nearby + if minetest.find_node_near(pos, 3, {"ignore"}) then + return + end + + -- Turn it back into base if it is already dry and no plant/seed + -- is on top of it + if wet_lvl == 0 then + if minetest.get_item_group(nn.name, "plant") == 0 and + minetest.get_item_group(nn.name, "seed") == 0 then + node.name = soil.base + minetest.set_node(pos, node) + end + + -- If it is wet turn it back into dry soil + elseif wet_lvl == 1 then + node.name = soil.dry + minetest.set_node(pos, node) end end, }) @@ -233,13 +249,15 @@ minetest.register_abm({ -- Make default:grass_* occasionally drop wheat seed for i = 1, 5 do - minetest.override_item("default:grass_"..i, {drop = { - max_items = 1, - items = { - {items = {"farming:seed_wheat"}, rarity = 5}, - {items = {"default:grass_1"}}, + minetest.override_item("default:grass_" .. i, { + drop = { + max_items = 1, + items = { + {items = {"farming:seed_wheat"}, rarity = 5}, + {items = {"default:grass_1"}}, + } } - }}) + }) end @@ -250,13 +268,15 @@ end -- This source is kept for now to avoid disruption but should probably be -- removed in future as players get used to the new source. -minetest.override_item("default:junglegrass", {drop = { - max_items = 1, - items = { - {items = {"farming:seed_cotton"}, rarity = 8}, - {items = {"default:junglegrass"}}, +minetest.override_item("default:junglegrass", { + drop = { + max_items = 1, + items = { + {items = {"farming:seed_cotton"}, rarity = 8}, + {items = {"default:junglegrass"}}, + } } -}}) +}) -- Wild cotton as a source of cotton seed