279 lines
8.9 KiB
Lua
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
|