mods that hopefully work for a TechAge-based pack
This commit is contained in:
1 parent
4e6b08d194
commit
c27db2a537
6234 files changed
+375578
No files matched your search
@@ -0,0 +1,458 @@
|
||||
-- this file contains the code that is used by scythes
|
||||
|
||||
local is_farming_redo = core.get_modpath("farming") ~= nil and farming ~= nil and farming.mod == "redo"
|
||||
local mod_mcl_farming = core.get_modpath("mcl_farming") ~= nil
|
||||
local is_voxelibre = mod_mcl_tools and (mcl_tools == nil)
|
||||
local S = farmtools.i18n
|
||||
|
||||
local DEFAULT_SCYTHE_USES = 90
|
||||
|
||||
local farming_redo_exceptions = {
|
||||
-- this is the list of plants that don't drop their seeds when harvested
|
||||
-- instead, the player normally has to craft the seeds from the item dropped during harvesting
|
||||
-- however, this causes a problem with scythes, since they require the player to have the seeds
|
||||
-- in the inventory to replant the plant after harvesting.
|
||||
-- the approach taken here is to automatically craft the seeds from the item when no seed is
|
||||
-- available.
|
||||
-- for that, we need the list below
|
||||
["farming:garlic_clove"] = { { item = "farming:garlic", seeds_in_item = 8 } },
|
||||
["farming:seed_sunflower"] = { { item = "farming:sunflower", seeds_in_item = 5 } },
|
||||
["farming:peppercorn"] = {
|
||||
{ item = "farming:pepper", seeds_in_item = 1 },
|
||||
{ item = "farming:pepper_red", seeds_in_item = 1 },
|
||||
{ item = "farming:pepper_yellow", seeds_in_item = 1 },
|
||||
},
|
||||
["farming:pumpkin_slice"] = { { item = "farming:pumpkin_8", seeds_in_item = 4 } },
|
||||
["farming:melon_slice"] = { { item = "farming:melon_8", seeds_in_item = 4 } },
|
||||
}
|
||||
|
||||
local item_craftable_to_seeds_in_inv = function(seeds, invref)
|
||||
-- this function should run when seeds required to replant the harvested plant are
|
||||
-- not available in the player inventory
|
||||
-- it checks whether an item from the inventory can be crafted into the required seeds and
|
||||
-- returns the item name along the number of seeds that can be crafted from it if it is the case
|
||||
|
||||
if is_farming_redo and farming_redo_exceptions[seeds] ~= nil then
|
||||
for _, def in pairs(farming_redo_exceptions[seeds]) do
|
||||
if invref:contains_item("main", def) then
|
||||
return def
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local get_item_group = function(def, group)
|
||||
if def == nil or def.groups == nil or def.groups[group] == nil then
|
||||
return 0
|
||||
end
|
||||
return def.groups[group]
|
||||
end
|
||||
|
||||
local get_plant_definition = function(plant)
|
||||
-- this function returns the plant definition from the farming API
|
||||
|
||||
-- x_farming has its own API
|
||||
-- if the plant is registered in the x_farming API, we return its definition
|
||||
local modname = plant:split(":")[1] or ""
|
||||
if modname == "x_farming" then
|
||||
-- note that we do not check whether x_farming is enabled,
|
||||
-- because if there is an item whose modname is x_farming,
|
||||
-- it means that the mod is enabled.
|
||||
-- if this is incorrect or may lead to bugs, please let me know :)
|
||||
local name = plant:split(":")[2] or ""
|
||||
local pname = name:gsub("(.*)_.*$", "%1")
|
||||
return x_farming.registered_plants[pname]
|
||||
end
|
||||
|
||||
-- next we need to check whether the farming API is provided by the default
|
||||
-- farming mod or by the Farming Redo version
|
||||
if is_farming_redo then
|
||||
-- if we are using Farming Redo, we need to iterate through the registered plants
|
||||
-- to check if the given plant is registered. if it is, we return its definition
|
||||
for _, data in pairs(farming.registered_plants) do
|
||||
if data.crop == plant then
|
||||
return data
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- if no definition was found previously, we try to see whether the plant can be found
|
||||
-- through the farming API (without checking whether it is the one from the default farming mod
|
||||
-- or the Redo mod, since the latter is fully compatible with the default one).
|
||||
-- if a plant is found, we return its definition
|
||||
if farming ~= nil and farming.registered_plants[plant] ~= nil then
|
||||
return farming.registered_plants[plant]
|
||||
end
|
||||
|
||||
-- finally, if no definition was found as of yet, we try one last time to see if the plant was
|
||||
-- registered without its mod name, and return the result.
|
||||
-- if no plant is found, the result will be nil.
|
||||
local name = plant:split(":")[2] or ""
|
||||
local pname = name:gsub("(.*)_.*$", "%1")
|
||||
if mod_mcl_farming then
|
||||
return mcl_farming.plant_lists["plant_" .. pname]
|
||||
end
|
||||
if farming ~= nil then
|
||||
return farming.registered_plants[pname]
|
||||
else
|
||||
return ""
|
||||
end
|
||||
end
|
||||
|
||||
local get_seed_name = function(plant)
|
||||
-- this function returns the plant's seeds using various methods
|
||||
-- it should be executed only IF WE KNOW that the plant exists
|
||||
-- (i.e. the function get_plant_definition returned a value that is different from nil)
|
||||
|
||||
-- TODO: maybe we could optimize this function by passing the plant definition directly,
|
||||
-- instead of having to use the API two times.
|
||||
|
||||
-- if Farming Redo is enabled, we can use its API to find the seeds
|
||||
-- (as long as the plant was registered through the farming API)
|
||||
-- if we find one, we return the seed name
|
||||
if is_farming_redo then
|
||||
for _, data in pairs(farming.registered_plants) do
|
||||
if data.crop == plant then
|
||||
return data.seed
|
||||
end
|
||||
end
|
||||
end
|
||||
if mod_mcl_farming then
|
||||
local orig_plant = plant
|
||||
local seeds = plant
|
||||
local this_seeds = core.registered_craftitems[seeds]
|
||||
-- technically this is for other mods too but it covers mineclonia
|
||||
local suffixes = { "_seeds", "_seed", "_item", "" }
|
||||
local continue = true
|
||||
local x = 0
|
||||
while this_seeds == nil and continue do
|
||||
for i, suffix in ipairs(suffixes) do
|
||||
seeds = plant .. suffix
|
||||
this_seeds = core.registered_craftitems[seeds]
|
||||
if this_seeds ~= nil then
|
||||
-- return plain text name of item
|
||||
return seeds
|
||||
end
|
||||
x = x + 1
|
||||
if x == 1 then
|
||||
-- strip "_plant" and try again
|
||||
plant = plant:gsub("_plant.*$", "")
|
||||
elseif x > 2 then
|
||||
continue = false
|
||||
end
|
||||
end
|
||||
end
|
||||
-- if we get here, there is no seed we could find.
|
||||
core.log("warning", "No mcl-based seeds calculated for item " .. orig_plant)
|
||||
end
|
||||
|
||||
-- if no plant was found previously, we try to see whether the plant can be found
|
||||
-- through the farming API (without checking whether it is the one from the default farming mod
|
||||
-- or the Redo mod, since the latter is fully compatible with the default one).
|
||||
-- if a plant is found and if its definition contains a "seed" property, we return its seed name
|
||||
if farming.registered_plants[plant] ~= nil and farming.registered_plants[plant].seed ~= nil then
|
||||
return farming.registered_plants[plant].seed
|
||||
end
|
||||
|
||||
-- finally, if no plant was found as of yet, we return an item named exactly like the plant,
|
||||
-- but with "seed_" appended right before the plant name
|
||||
local mod = plant:split(":")[1]
|
||||
local name = plant:split(":")[2]
|
||||
local pname = name:gsub("(.*)_.*$", "%1")
|
||||
return mod .. ":seed_" .. pname
|
||||
end
|
||||
|
||||
local harvest_and_replant = function(pos, user)
|
||||
-- this is the function used to harvest and replant a single node using the scythe
|
||||
-- it returns a boolean value that indicates whether it was able to harvest (and hopefully replant)
|
||||
-- the node at a given position
|
||||
|
||||
local playername = user:get_player_name()
|
||||
local node = core.get_node(pos)
|
||||
local node_id = node.name:gsub("(.*)_.*$", "%1")
|
||||
if mod_mcl_farming then
|
||||
node_id = node.name
|
||||
end
|
||||
|
||||
-- try to get a plant definition for the given node
|
||||
-- if none is found, we will exit this function and return false
|
||||
local plantdef = get_plant_definition(node_id)
|
||||
if plantdef == nil then
|
||||
return false
|
||||
end
|
||||
|
||||
-- try to guess current stage of the plant from its node ID
|
||||
local stage = tonumber(node.name:gsub(".*_(.*)$", "%1") or 0)
|
||||
-- if either:
|
||||
-- - the definition does not contain a "steps" property to indicate how many stages the plant goes through when maturing
|
||||
-- - the current stage of the plant couldn't be detected from the node ID
|
||||
-- we exit the function by returning false
|
||||
if mod_mcl_farming then
|
||||
if plantdef.full_grown == nil then
|
||||
return false
|
||||
elseif plantdef.full_grown == node_id then
|
||||
else
|
||||
return false
|
||||
end
|
||||
else
|
||||
-- now we test the current stage of the plant against its total number of steps
|
||||
-- if the value of "stage" is lower than "steps", it means that the plant isn't yet ready for harvesting
|
||||
-- thus, we exit this function and return false
|
||||
if plantdef.steps == nil or stage == nil or stage < plantdef.steps then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- if the node is protected or the given user isn't allowed to break it,
|
||||
-- we record the violation attempt and return false
|
||||
if core.is_protected(pos, playername) then
|
||||
core.record_protection_violation(pos, playername)
|
||||
return false
|
||||
end
|
||||
|
||||
-- otherwise we can go ahead and break the plant, while playing a nice sound
|
||||
core.node_dig(pos, node, user)
|
||||
core.sound_play("default_dig_snappy", { pos = pos, gain = 0.5, max_hear_distance = 8 }, true)
|
||||
|
||||
-- once the plant is broken, we need to replant it
|
||||
core.after(0, function()
|
||||
-- this function replants the plant after is is harvested
|
||||
-- it also takes care of removing the correct amount of seeds from the player inventory
|
||||
|
||||
local invref = user:get_inventory()
|
||||
local seeds = get_seed_name(node_id)
|
||||
|
||||
-- this is required to prevent trellis and bean poles duplication
|
||||
if is_farming_redo then
|
||||
if node_id == "farming:grapes" then
|
||||
if invref:contains_item("main", "farming:trellis") then
|
||||
invref:remove_item("main", "farming:trellis")
|
||||
end
|
||||
elseif node_id == "farming:beanpole" then
|
||||
if invref:contains_item("main", "farming:beanpole") then
|
||||
invref:remove_item("main", "farming:beanpole")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- if seeds are not present in the inventory, and if no item from the inventory can be
|
||||
-- crafted into the seeds, we won't go further. it means that we cannot replant the plant
|
||||
local item_in_inv = item_craftable_to_seeds_in_inv(seeds, invref)
|
||||
if (not mod_mcl_farming) and (core.get_node(pos).name ~= "air" or not invref:contains_item("main", seeds)) then
|
||||
if item_in_inv == nil then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
-- we will only remove seeds from the player inventory if they are not in creative mod
|
||||
if not core.is_creative_enabled(playername) and not mod_mcl_farming then
|
||||
if not invref:contains_item("main", seeds) and item_in_inv ~= nil then
|
||||
-- if no seed is found in the inventory but an item from the inventory can be crafted
|
||||
-- into the seeds:
|
||||
-- first remove one of these items from the inventory
|
||||
invref:remove_item("main", item_in_inv.item)
|
||||
-- then add the number of seeds normally returned by the craft, less one (because we
|
||||
-- use one to replant the plant)
|
||||
local stack = ItemStack(seeds .. " " .. item_in_inv.seeds_in_item)
|
||||
invref:add_item("main", stack)
|
||||
else
|
||||
-- if seeds are already present in the inventory, then just remove one of them
|
||||
-- from the inventory
|
||||
invref:remove_item("main", seeds)
|
||||
end
|
||||
end
|
||||
|
||||
-- finally, we need to replant the crop
|
||||
-- it is a bit different, depending on the version of the farming mod used.
|
||||
-- TODO: the behavior of this code depends on whether Farming Redo is used, but
|
||||
-- not on the mod that provides the plant. If the plant is from x_farming, the behavior
|
||||
-- will be different if Farming Redo is enabled and if it is disabled.
|
||||
if is_farming_redo then
|
||||
-- plant first crop for farming redo
|
||||
local crop_name = node_id .. "_1"
|
||||
local crop_def = core.registered_nodes[crop_name]
|
||||
if crop_def == nil then
|
||||
return
|
||||
end
|
||||
core.set_node(pos, { name = crop_name, param2 = crop_def.place_param2 })
|
||||
else
|
||||
if mod_mcl_farming then
|
||||
-- plant seeds for Mineclonia farming
|
||||
-- try level 0
|
||||
local level1 = node_id .. "_0"
|
||||
local crop_def = core.registered_nodes[level1]
|
||||
if crop_def == nil then
|
||||
level1 = node_id .. "_1"
|
||||
crop_def = core.registered_nodes[level1]
|
||||
end
|
||||
-- keep the same paramtype2 = "meshoptions" from lua_api.md
|
||||
core.set_node(pos, { name = level1, param2 = node.param2 })
|
||||
-- need to remove seed afterwards for Mineclonia, because it dropped seeds
|
||||
-- and not immediately placed them in player inventory. 2 seconds
|
||||
-- ought to be enough for the player to walk over the seeds.
|
||||
core.after(2, function()
|
||||
if not core.is_creative_enabled(playername) then
|
||||
invref:remove_item("main", { name = seeds, count = 1 })
|
||||
end
|
||||
end)
|
||||
else
|
||||
-- plant seeds for MTG farming
|
||||
core.set_node(pos, { name = seeds, param2 = 1 })
|
||||
end
|
||||
-- timer values taken from farming mod (see tick function in api.lua)
|
||||
core.get_node_timer(pos):start(math.random(166, 286))
|
||||
end
|
||||
end)
|
||||
-- finally, we return true, because we were able to harvest (and hopefully replant the crop)
|
||||
return true
|
||||
end
|
||||
|
||||
farmtools.scythe_on_use = function(itemstack, user, pointed_thing, uses)
|
||||
-- this function is called when the scythe is used on a node
|
||||
|
||||
-- if the scythe is used when the player isn't pointing at anything, we won't do anything
|
||||
if pointed_thing == nil then
|
||||
return
|
||||
end
|
||||
|
||||
-- if pointing at an object/entity, punch it with the scythe
|
||||
local itemdef = itemstack:get_definition()
|
||||
if pointed_thing.type == "object" then
|
||||
local tool_capabilities = itemstack:get_tool_capabilities()
|
||||
local meta = itemstack:get_meta()
|
||||
local last_punch = meta:get_float("last_punch") or 0
|
||||
local now = core.get_gametime()
|
||||
meta:set_float("last_punch", now)
|
||||
pointed_thing.ref:punch(user, now - last_punch, tool_capabilities)
|
||||
end
|
||||
|
||||
-- if pointing at something other than a node, don't go further
|
||||
if pointed_thing.type ~= "node" then
|
||||
return
|
||||
end
|
||||
|
||||
-- the scythe group allows us to specify how big the area to harvest should be
|
||||
-- if scythe = 2, then the area is a 3x3 area
|
||||
-- if scythe = 3, then the area is a 5x5 area
|
||||
-- and so on...
|
||||
-- this does not correspond to the actual level of the scythe,
|
||||
-- unfortunately we need to keep it this way because doing otherwise would break compatibility with
|
||||
-- mods that are using the scythe group as provided by the original sickles mod
|
||||
-- in short: the value of the scythe group = level of the scythe + 1
|
||||
local range = (get_item_group(itemdef, "scythe") or 1) - 1
|
||||
|
||||
-- TODO: is all of the code below really supposed to be executed when pointed_thing.type == "object"?
|
||||
-- if not, we should maybe wrap this in an if pointed_thing.type == "node" condition
|
||||
|
||||
-- get the position of the node that the user is pointing at
|
||||
local pos = pointed_thing.under
|
||||
|
||||
-- try to harvest (and replant) the node
|
||||
local harvested = harvest_and_replant(pos, user)
|
||||
|
||||
-- if it couldn't be harvested, we won't go any further
|
||||
if not harvested then
|
||||
return
|
||||
end
|
||||
|
||||
-- this will be required for wearing tools depending on the number of nodes harvested
|
||||
local total_nodes_changed = 1
|
||||
|
||||
-- otherwise we harvest and replant the adjacent nodes within the range specified in the item definition
|
||||
if range > 0 then
|
||||
local pos1 = vector.add(pos, { x = -range, y = 0, z = -range })
|
||||
local pos2 = vector.add(pos, { x = range, y = 0, z = range })
|
||||
local positions = core.find_nodes_in_area(pos1, pos2, "group:plant")
|
||||
for _, check_pos in ipairs(positions) do
|
||||
if pos ~= check_pos then
|
||||
if harvest_and_replant(check_pos, user) then
|
||||
total_nodes_changed = total_nodes_changed + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- wear out tools if the player is not in creative mode
|
||||
local playername = user and user:get_player_name() or ""
|
||||
if not core.is_creative_enabled(playername) then
|
||||
if mod_mcl_farming then
|
||||
local calc_uses = get_item_group(itemdef, "scythe_uses")
|
||||
-- if mineclonia
|
||||
if not is_voxelibre then
|
||||
calc_uses = mcl_util.calculate_durability(itemstack) or calc_uses
|
||||
end
|
||||
itemstack:add_wear_by_uses(calc_uses / total_nodes_changed)
|
||||
else
|
||||
itemstack:add_wear_by_uses(uses / total_nodes_changed)
|
||||
end
|
||||
end
|
||||
|
||||
-- finally return the worn out scythe
|
||||
return itemstack
|
||||
end
|
||||
|
||||
farmtools.register_scythe = function(name, def)
|
||||
-- this function is used for registering new scythes in the easiest way possible
|
||||
|
||||
-- register new scythes in your mod's namespace
|
||||
if name:sub(1, 1) ~= ":" then
|
||||
name = ":" .. name
|
||||
end
|
||||
|
||||
-- check definition table
|
||||
if def.description == nil then
|
||||
def.description = S("Scythe")
|
||||
end
|
||||
if def.inventory_image == nil then
|
||||
def.inventory_image = "unknown_item.png"
|
||||
end
|
||||
if def.max_uses == nil then
|
||||
def.max_uses = DEFAULT_SCYTHE_USES
|
||||
end
|
||||
if def.level == nil then
|
||||
def.level = 1
|
||||
end
|
||||
-- support the definition providing groups, primarily for enchantability for voxelibre
|
||||
local groups = { scythe = def.level + 1 }
|
||||
if def.groups then
|
||||
for k, v in pairs(def.groups) do
|
||||
groups[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
-- now register the tool through the Luanti API
|
||||
core.register_tool(name, {
|
||||
description = def.description,
|
||||
inventory_image = def.inventory_image,
|
||||
tool_capabilities = {
|
||||
full_punch_interval = 1.2,
|
||||
damage_groups = { fleshy = 5 },
|
||||
punch_attack_uses = def.max_uses * 2,
|
||||
},
|
||||
range = 12,
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
return farmtools.scythe_on_use(itemstack, user, pointed_thing, def.max_uses)
|
||||
end,
|
||||
groups = groups,
|
||||
sound = { breaks = "default_tool_breaks" },
|
||||
_mcl_uses = groups["scythe_uses"] or def.max_uses,
|
||||
})
|
||||
|
||||
-- now register recipe
|
||||
if def.recipe then
|
||||
core.register_craft({
|
||||
output = name:sub(2),
|
||||
recipe = def.recipe,
|
||||
})
|
||||
elseif def.material then
|
||||
core.register_craft({
|
||||
output = name:sub(2),
|
||||
recipe = {
|
||||
{ "", def.material, def.material },
|
||||
{ def.material, "", "group:stick" },
|
||||
{ "", "", "group:stick" },
|
||||
},
|
||||
})
|
||||
end
|
||||
end
|
||||
Reference in new issue
Block a user