Christian Sirolli 422865d44e
bug fix
2024-12-26 16:28:37 -05:00

279 lines
8.9 KiB
Lua

knapping = {
config = {
-- time after which the knapping process gets aborted,
-- to prevent players from cluttering a world with thousands of entities
timeout = core.settings:get("knapping_timeout") or 600,
-- how many knapping processes can run simultaneously,
-- again, to prevent players from cluttering a world with entities
max_crafts = core.settings:get("knapping_max_crafts") or 25
},
path = core.get_modpath('knapping'),
S = core.get_translator('knapping'),
default_recipes = {
pick = {
{ 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 1, 1, 0, 0, 0 },
{ 0, 1, 1, 1, 1, 1, 1, 0 },
{ 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 1, 0, 0, 1, 1, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 1 },
{ 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0 },
},
axe = {
{ 0, 0, 1, 1, 0, 0, 0, 0 },
{ 0, 1, 1, 1, 1, 0, 0, 0 },
{ 0, 1, 1, 1, 1, 1, 0, 0 },
{ 0, 1, 1, 1, 1, 1, 1, 0 },
{ 0, 1, 1, 1, 1, 1, 1, 0 },
{ 0, 1, 1, 1, 0, 1, 0, 0 },
{ 0, 1, 1, 1, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 0, 0 },
},
shovel = {
{ 0, 0, 0, 1, 1, 0, 0, 0 },
{ 0, 0, 1, 1, 1, 1, 0, 0 },
{ 0, 1, 1, 1, 1, 1, 1, 0 },
{ 0, 1, 1, 1, 1, 1, 1, 0 },
{ 0, 1, 1, 1, 1, 1, 1, 0 },
{ 0, 1, 1, 1, 1, 1, 1, 0 },
{ 0, 1, 1, 1, 1, 1, 1, 0 },
{ 0, 0, 1, 0, 0, 1, 0, 0 },
},
sword = {
{ 0, 0, 0, 0, 0, 0, 1, 1 },
{ 0, 0, 0, 0, 0, 1, 1, 1 },
{ 0, 0, 0, 0, 1, 1, 1, 0 },
{ 0, 0, 0, 1, 1, 1, 0, 0 },
{ 0, 0, 1, 1, 1, 0, 0, 0 },
{ 0, 1, 1, 1, 0, 0, 0, 0 },
{ 1, 1, 1, 0, 0, 0, 0, 0 },
{ 1, 1, 0, 0, 0, 0, 0, 0 },
},
},
registered_recipes = {},
registered_callbacks = {},
crafting_processes = {},
}
local fallback_texture = "knapping_stone.png"
local initial_properties = {
visual = "cube",
collisionbox = { -0.5 / 8, -0.5 / 8, -0.5 / 8, 0.5 / 8, 0.5 / 8, 0.5 / 8 },
textures = { fallback_texture, fallback_texture, fallback_texture,
fallback_texture, fallback_texture, fallback_texture },
visual_size = { x = 1 / 8, y = 1 / 8, z = 1 / 8 },
physical = false,
static_save = false,
}
core.register_entity('knapping:stone_piece_go', {
initial_properties = initial_properties,
on_punch = function(self, puncher)
if puncher:get_wielded_item():get_name() ~= self.material then return end
self.get_removed()
self.object:remove()
core.sound_play("thunk", { gain = 0.3, to_player = puncher:get_player_name() }, true)
end,
})
core.register_entity('knapping:stone_piece', {
initial_properties = initial_properties,
})
local function get_empty_slot(t)
local i = 1
while t[i] do
i = i + 1
end
return i
end
local function clear_craft(id, cp_table)
-- first check if this crafting process even still exists
if not knapping.crafting_processes[id] then return end
if cp_table and knapping.crafting_processes[id] ~= cp_table then return end
if cp_table and knapping.crafting_processes[id] == cp_table then
core.chat_send_player(cp_table.player,
knapping.S("The knapping process you started at: @1 crumbled away due to time.",
core.pos_to_string(cp_table.pos)))
end
local pos = knapping.crafting_processes[id].pos
local bits = core.get_objects_inside_radius(pos, 1)
for _, v in pairs(bits) do
if not v:is_player() and v:get_luaentity().id == id then
v:remove()
end
end
knapping.crafting_processes[id] = nil
end
local function finish_craft(id)
local crafting_process = knapping.crafting_processes[id]
local pos = crafting_process.pos
core.add_item(pos, crafting_process.recipe.output)
for _, func in pairs(knapping.registered_callbacks) do
func(crafting_process.recipe, pos, crafting_process.player)
end
clear_craft(id)
end
local function place_knapping_plane(id)
local crafting_process = knapping.crafting_processes[id]
local recipe = crafting_process.recipe
local node_pos = crafting_process.pos
local start_pos = vector.add(node_pos, -7 / 16)
-- instead of matching the in-world pattern to the recipe, we just count up
-- how many diggable pieces there are and finnish once they are gone
local to_remove = 0
for x = 1, 8 do
for z = 1, 8 do
local pos = { x = start_pos.x + (1 / 8) * (x - 1), y = start_pos.y, z = start_pos.z + (1 / 8) * (z - 1) }
local texture = recipe.texture and recipe.texture .. "^[sheet:8x8:" .. x - 1 .. "," .. 8 - z or
"knapping_stone.png"
if recipe.recipe[x][z] == 1 then
local objref = core.add_entity(pos, 'knapping:stone_piece')
objref:set_properties({ textures = { texture, texture, texture, texture, texture, texture } })
local luaent = objref:get_luaentity()
-- used during cleanup so recipes in adjacent nodes don't get messed up
luaent.id = id
else
to_remove = to_remove + 1
local objref = core.add_entity(pos, 'knapping:stone_piece_go')
texture = texture .. "^[multiply:#b0b0b0" -- darken texture to distinguish the pattern
objref:set_properties({ textures = { texture, texture, texture, texture, texture, texture } })
local luaent = objref:get_luaentity()
luaent.id = id
luaent.material = recipe.input
luaent.get_removed = function()
to_remove = to_remove - 1
if to_remove == 0 then
finish_craft(id)
end
end
end
end
end
-- so the map doesn't get cluttered, this isn't supposed to be a permanent decoration after all
core.after(knapping.config.timeout, clear_craft, id, crafting_process)
end
local function get_knapping_formspec(itemname, pos)
local spos = core.pos_to_string(pos)
local recipes = knapping.registered_recipes[itemname]
local formspec = {}
formspec[#formspec + 1] = "size["
formspec[#formspec + 1] = #recipes
formspec[#formspec + 1] = ",1]"
local x = 0
for _, v in pairs(recipes) do
formspec[#formspec + 1] = "item_image_button["
formspec[#formspec + 1] = x
formspec[#formspec + 1] = ",0.125;1,1;"
formspec[#formspec + 1] = v.output
formspec[#formspec + 1] = ";"
-- is there a a way to avoid passing the position through the formspec?
formspec[#formspec + 1] = itemname .. "|" .. v.output .. "|" .. spos
formspec[#formspec + 1] = ";]"
x = x + 1
end
return table.concat(formspec)
end
local function handle_input(player, formname, fields)
if formname ~= "knapping:choose_recipe" then return end
if fields.quit then return end
local player_name = player:get_player_name()
for k, v in pairs(fields) do
if v then
local a = k:find("|")
local b = k:find("|", a + 1)
local itemname, output, pos = k:sub(1, a - 1), k:sub(a + 1, b - 1), core.string_to_pos(k:sub(b + 1, -1))
local recipe
for _, v in pairs(knapping.registered_recipes[itemname]) do
if v.output == output then recipe = v end
end
core.close_formspec(player_name, formname)
local id = get_empty_slot(knapping.crafting_processes)
if id > knapping.config.max_crafts then
core.chat_send_player(player_name, knapping.S("There are too many knapping processes going on at once."))
core.chat_send_player(player_name, knapping.S("Try again in a few minutes."))
return true
end
knapping.crafting_processes[id] = {
pos = pos,
recipe = recipe,
player = player_name,
}
place_knapping_plane(id)
-- remove input item if not creative
if not core.settings:get_bool("creative_mode") and
not core.check_player_privs(player_name, { creative = true }) then
local itemstack = player:get_wielded_item()
itemstack:take_item()
player:set_wielded_item(itemstack)
end
end
end
return true
end
core.register_on_player_receive_fields(handle_input)
local function can_knapp(placer, pointed_thing)
if pointed_thing.under.y ~= pointed_thing.above.y - 1 then return false end
if core.get_node(pointed_thing.above).name ~= "air" then return false end
if core.registered_nodes[core.get_node(pointed_thing.under).name].walkable ~= true then return false end
if not placer:get_player_control().sneak then return false end
return true
end
local function add_knapping(itemname)
assert(core.registered_items[itemname],
"Trying to register a knapping recipe using the nonexistent item: '" .. itemname .. "'")
local og_on_place = core.registered_items[itemname].on_place
core.override_item(itemname, {
description = core.registered_items[itemname].description ..
core.colorize("#ababab", "\n" .. knapping.S("Use on the top of a surface while sneaking to start knapping.")),
on_place = function(itemstack, placer, pointed_thing)
if can_knapp(placer, pointed_thing) then
core.show_formspec(placer:get_player_name(), "knapping:choose_recipe",
get_knapping_formspec(itemname, pointed_thing.above))
else
return og_on_place and og_on_place(itemstack, placer, pointed_thing)
end
end,
})
end
function knapping.register_on_craft(func)
assert(type(func) == "function", "'register_on_craft' expects a function, got: '" .. type(func) .. "'")
table.insert(knapping.registered_callbacks, func)
end
function knapping.register_recipe(recipe)
if not knapping.registered_recipes[recipe.input] then
knapping.registered_recipes[recipe.input] = {}
add_knapping(recipe.input)
end
table.insert(knapping.registered_recipes[recipe.input], recipe)
end