865 lines
32 KiB
Lua
865 lines
32 KiB
Lua
------------------------------------------------------------------------------------------------------------------------
|
|
-- BASIC MACHINES MOD by rnd
|
|
-- Mod with basic simple automatization for Luanti
|
|
-- No background processing, just two abms (clock generator and generator), no other lag causing background processing
|
|
------------------------------------------------------------------------------------------------------------------------
|
|
-- (c) 2015-2016 rnd
|
|
-- Copyright (C) 2022-2025 мтест
|
|
-- See README.md for license details
|
|
|
|
local F, S = basic_machines.F, basic_machines.S
|
|
local mover_upgrade_max = basic_machines.properties.mover_upgrade_max
|
|
local machines_minstep = basic_machines.properties.machines_minstep
|
|
local machines_operations = basic_machines.properties.machines_operations
|
|
local machines_timer = basic_machines.properties.machines_timer
|
|
local mover_max_temp = math.max(1, basic_machines.settings.mover_max_temp)
|
|
local mover_no_large_stacks = basic_machines.settings.mover_no_large_stacks
|
|
local twodigits_float = basic_machines.twodigits_float
|
|
local temp_80P = mover_max_temp > 12 and math.ceil(mover_max_temp * 0.8)
|
|
local temp_15P = math.ceil(mover_max_temp * 0.15)
|
|
local vector_add = vector.add
|
|
local math_min = math.min
|
|
|
|
-- *** MOVER SETTINGS *** --
|
|
local mover = {
|
|
bonemeal_table = {
|
|
["bonemeal:bonemeal"] = true,
|
|
["bonemeal:fertiliser"] = true,
|
|
["bonemeal:mulch"] = true
|
|
},
|
|
|
|
-- list of chests with inventory named "main"
|
|
chests = {
|
|
["chests_2:chest_locked_x2"] = true,
|
|
["chests_2:chest_x2"] = true,
|
|
["default:chest"] = true,
|
|
["default:chest_locked"] = true,
|
|
["digilines:chest"] = true,
|
|
["protector:chest"] = true
|
|
},
|
|
|
|
-- define which nodes are dug up completely, like a tree
|
|
dig_up_table = {},
|
|
|
|
-- how hard it is to move blocks, default factor 1,
|
|
-- note: fuel cost is this multiplied by distance and divided by machines_operations
|
|
hardness = {
|
|
["bedrock2:bedrock"] = 999999,
|
|
["bedrock:bedrock"] = 999999,
|
|
["default:acacia_tree"] = 2,
|
|
["default:bush_leaves"] = 0.1,
|
|
["default:cloud"] = 999999,
|
|
["default:jungleleaves"] = 0.1,
|
|
["default:jungletree"] = 2,
|
|
["default:leaves"] = 0.1,
|
|
["default:obsidian"] = 20,
|
|
["default:pine_tree"] = 2,
|
|
["default:stone"] = 4,
|
|
["default:tree"] = 2,
|
|
["gloopblocks:basalt_cooled"] = 3,
|
|
["gloopblocks:obsidian_cooled"] = 20,
|
|
["gloopblocks:pumice_cooled"] = 2,
|
|
["itemframes:frame"] = 999999,
|
|
["itemframes:frame_invis"] = 999999,
|
|
["itemframes:pedestal"] = 999999,
|
|
["painting:canvasnode"] = 999999,
|
|
["painting:pic"] = 999999,
|
|
["statue:pedestal"] = 999999,
|
|
["x_farming:cocoa_1"] = 999999,
|
|
["x_farming:cocoa_2"] = 999999,
|
|
["x_farming:cocoa_3"] = 999999,
|
|
["x_farming:seed_rice"] = 999999,
|
|
|
|
-- move machines for free (mostly)
|
|
["basic_machines:ball_spawner"] = 0,
|
|
["basic_machines:battery_0"] = 0,
|
|
["basic_machines:battery_1"] = 0,
|
|
["basic_machines:battery_2"] = 0,
|
|
["basic_machines:clockgen"] = 999999, -- can only place clockgen by hand
|
|
["basic_machines:detector"] = 0,
|
|
["basic_machines:distributor"] = 0,
|
|
["basic_machines:generator"] = 999999, -- can only place generator by hand
|
|
["basic_machines:keypad"] = 0,
|
|
["basic_machines:light_off"] = 0,
|
|
["basic_machines:light_on"] = 0,
|
|
["basic_machines:mover"] = 0,
|
|
|
|
-- grief potential items need highest possible upgrades
|
|
["boneworld:acid_source_active"] = 5950,
|
|
["darkage:mud"] = 5950,
|
|
["default:lava_source"] = 5950, ["default:river_water_source"] = 5950, ["default:water_source"] = 5950,
|
|
["es:toxic_water_source"] = 5950, ["es:toxic_water_flowing"] = 5950,
|
|
["integral:sap"] = 5950, ["integral:weightless_water"] = 5950,
|
|
["underworlds:water_death_source"] = 5950, ["underworlds:water_poison_source"] = 5950,
|
|
|
|
-- farming operations are much cheaper
|
|
["farming:cotton_8"] = 1, ["farming:wheat_8"] = 1,
|
|
["farming:seed_cotton"] = 0.5, ["farming:seed_wheat"] = 0.5,
|
|
|
|
-- digging mese crystals more expensive
|
|
["mese_crystals:mese_crystal_ore1"] = 10,
|
|
["mese_crystals:mese_crystal_ore2"] = 10,
|
|
["mese_crystals:mese_crystal_ore3"] = 10,
|
|
["mese_crystals:mese_crystal_ore4"] = 10
|
|
},
|
|
|
|
-- set up nodes for harvest when digging: [nodename] = {what remains after harvest, harvest result}
|
|
harvest_table = {
|
|
["mese_crystals:mese_crystal_ore1"] = {"mese_crystals:mese_crystal_ore1", nil}, -- harvesting mese crystals
|
|
["mese_crystals:mese_crystal_ore2"] = {"mese_crystals:mese_crystal_ore1", "default:mese_crystal 1"},
|
|
["mese_crystals:mese_crystal_ore3"] = {"mese_crystals:mese_crystal_ore1", "default:mese_crystal 2"},
|
|
["mese_crystals:mese_crystal_ore4"] = {"mese_crystals:mese_crystal_ore1", "default:mese_crystal 3"}
|
|
},
|
|
|
|
-- list of nodes mover can't take from in inventory mode
|
|
-- node name = {list of bad inventories to take from} OR node name = true to ban all inventories
|
|
limit_inventory_table = {
|
|
["basic_machines:autocrafter"] = {["output"] = 1, ["recipe"] = 1},
|
|
["basic_machines:battery_0"] = {["upgrade"] = 1},
|
|
["basic_machines:battery_1"] = {["upgrade"] = 1},
|
|
["basic_machines:battery_2"] = {["upgrade"] = 1},
|
|
["basic_machines:constructor"] = {["recipe"] = 1},
|
|
["basic_machines:generator"] = {["upgrade"] = 1},
|
|
["basic_machines:grinder"] = {["upgrade"] = 1},
|
|
["basic_machines:mover"] = true,
|
|
["moreblocks:circular_saw"] = true,
|
|
["smartshop:shop"] = true,
|
|
["xdecor:workbench"] = {["forms"] = 1, ["input"] = 1}
|
|
},
|
|
|
|
-- list of mover modes
|
|
modes = {
|
|
_count = 0,
|
|
_tr_table = {}
|
|
},
|
|
|
|
-- list of objects that can't be teleported with mover
|
|
no_teleport_table = {
|
|
[""] = true,
|
|
["3d_armor_stand:armor_entity"] = true,
|
|
["__builtin:item"] = true,
|
|
["itemframes:item"] = true,
|
|
["machines:posN"] = true,
|
|
["machines:posS"] = true,
|
|
["painting:paintent"] = true,
|
|
["painting:picent"] = true,
|
|
["shield_frame:shield_entity"] = true,
|
|
["signs_lib:text"] = true,
|
|
["statue:statue"] = true,
|
|
["x_farming:stove_food"] = true,
|
|
["xdecor:f_item"] = true
|
|
},
|
|
|
|
-- set up nodes for plant with reverse on and filter set
|
|
-- for example seed -> plant, [nodename] = plant_name OR [nodename] = true
|
|
plants_table = {},
|
|
|
|
-- reverse upgrades list, as follow: [id] = upgrade_item
|
|
revupgrades = {},
|
|
|
|
-- list of items for the upgrade slot
|
|
upgrades = {
|
|
["default:mese"] = {id = 1, max = mover_upgrade_max}, -- for all modes
|
|
["default:diamondblock"] = {id = 2, max = 99}, -- object mode
|
|
["basic_machines:mover"] = {id = 3, max = 74} -- normal and dig modes
|
|
}
|
|
}
|
|
|
|
if basic_machines.use_default then
|
|
mover.dig_up_table = {
|
|
["default:acacia_tree"] = {h = 6, r = 2}, -- acacia trees grow wider than others
|
|
["default:aspen_tree"] = {h = 10, r = 0},
|
|
["default:cactus"] = {h = 5, r = 2},
|
|
["default:jungletree"] = {h = 11}, -- not emergent jungle tree
|
|
["default:papyrus"] = {h = 3, r = 0},
|
|
["default:pine_tree"] = {h = 13, r = 0},
|
|
["default:tree"] = {h = 4, r = 1}
|
|
}
|
|
end
|
|
|
|
-- cool_trees
|
|
local cool_trees = { -- all but pineapple
|
|
{"baldcypress", h = 17, r = 5}, -- why the trunk isn't centered at the sapling position
|
|
{"bamboo", h = 10, r = 0},
|
|
{"birch", h = 4, r = 0, d = 1},
|
|
{"cacaotree", h = 3, r = 0},
|
|
{"cherrytree", h = 5},
|
|
{"chestnuttree", h = 10, r = 5},
|
|
{"clementinetree", h = 3, r = 0},
|
|
{"ebony", h = 14, r = 4},
|
|
{"hollytree", h = 10, r = 3},
|
|
{"jacaranda", h = 6, r = 0},
|
|
{"larch", h = 13, r = 2},
|
|
{"lemontree", h = 3},
|
|
{"mahogany", h = 15, r = 2},
|
|
{"maple", h = 7, r = 3},
|
|
{"oak", h = 14, r = 4},
|
|
{"palm", h = 4, r = 2},
|
|
{"plumtree", h = 8, r = 3, d = 1},
|
|
{"pomegranate", h = 2, r = 0},
|
|
{"sequoia", h = 46, r = 7, d = 4},
|
|
{"willow", h = 11, r = 3}
|
|
}
|
|
|
|
for _, cool_tree in ipairs(cool_trees) do
|
|
local name = cool_tree[1]
|
|
if minetest.global_exists(name) or minetest.get_modpath(name) then
|
|
mover.dig_up_table[name .. ":trunk"] = {h = cool_tree.h, r = cool_tree.r, d = cool_tree.d}
|
|
end
|
|
end
|
|
--
|
|
|
|
if minetest.global_exists("farming") then
|
|
for name, plant in pairs(farming.registered_plants or {}) do
|
|
if farming.mod == "redo" then
|
|
mover.plants_table[plant.seed] = plant.crop .. "_1"
|
|
else
|
|
local seed = "farming:seed_" .. name
|
|
if minetest.registered_nodes[seed] then
|
|
mover.plants_table[seed] = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if minetest.global_exists("x_farming") then
|
|
for name, _ in pairs(x_farming.registered_plants or {}) do
|
|
local seed = "x_farming:seed_" .. name
|
|
if minetest.registered_nodes[seed] then
|
|
mover.plants_table[seed] = true
|
|
end
|
|
end
|
|
mover.plants_table["x_farming:seed_rice"] = nil
|
|
if minetest.registered_nodes["x_farming:seed_salt"] then
|
|
mover.plants_table["x_farming:seed_salt"] = true
|
|
end
|
|
end
|
|
|
|
for upgrade_item, upgrade in pairs(mover.upgrades) do
|
|
mover.revupgrades[upgrade.id] = upgrade_item
|
|
end
|
|
|
|
-- return either content of a given setting or all settings
|
|
basic_machines.get_mover = function(setting)
|
|
local def
|
|
if setting and mover[setting] then
|
|
def = mover[setting]
|
|
else
|
|
def = mover
|
|
end
|
|
return table.copy(def)
|
|
end
|
|
|
|
-- add/replace values as table of an existing setting
|
|
basic_machines.set_mover = function(setting, def)
|
|
if not setting or not mover[setting] then return end
|
|
for k, v in pairs(def) do
|
|
mover[setting][k] = v
|
|
end
|
|
end
|
|
-- *** END OF MOVER SETTINGS *** --
|
|
|
|
-- load files
|
|
local MP = minetest.get_modpath("basic_machines") .. "/"
|
|
|
|
dofile(MP .. "mover_common.lua")
|
|
dofile(MP .. "mover_normal_mode.lua")
|
|
dofile(MP .. "mover_dig_mode.lua")
|
|
dofile(MP .. "mover_drop_mode.lua")
|
|
dofile(MP .. "mover_object_mode.lua")
|
|
dofile(MP .. "mover_inventory_mode.lua")
|
|
dofile(MP .. "mover_transport_mode.lua")
|
|
|
|
|
|
-- MOVER --
|
|
minetest.register_chatcommand("mover_intro", {
|
|
description = S("Toggle mover introduction"),
|
|
privs = {interact = true},
|
|
func = function(name, _)
|
|
local player = minetest.get_player_by_name(name); if not player then return end
|
|
local player_meta = player:get_meta()
|
|
if player_meta:get_int("basic_machines:mover_intro") == 1 then
|
|
player_meta:set_int("basic_machines:mover_intro", 3)
|
|
minetest.chat_send_player(name, S("Mover introduction disabled"))
|
|
else
|
|
player_meta:set_int("basic_machines:mover_intro", 1)
|
|
minetest.chat_send_player(name, S("Mover introduction enabled"))
|
|
end
|
|
end
|
|
})
|
|
|
|
local mover_upgrades = mover.upgrades
|
|
local mover_revupgrades = mover.revupgrades
|
|
local mover_modes = mover.modes
|
|
local get_distance = basic_machines.get_distance
|
|
|
|
local function pos1_checks(pos, owner)
|
|
if minetest.is_protected(pos, owner) then -- protection check
|
|
return true
|
|
end
|
|
local node = minetest.get_node(pos)
|
|
return false, node, node.name
|
|
end
|
|
|
|
local function pos1list_checks(pos, length_pos, owner, upgrade, meta)
|
|
local is_protected = minetest.is_protected
|
|
local node, node_name, count = {}, {}, 0
|
|
local mover_hardness, hardness = mover.hardness, 0
|
|
local maxpower -- battery maximum power output
|
|
for i = 1, length_pos do
|
|
local posi = pos[i]
|
|
if is_protected(posi, owner) then -- protection check
|
|
return true
|
|
else
|
|
local nodei = minetest.get_node(posi)
|
|
local nodei_name = nodei.name
|
|
if nodei_name == "air" or nodei_name == "ignore" then
|
|
pos[i] = {}; count = count + 1
|
|
elseif upgrade == -1 then -- admin, just add nodes
|
|
node[i], node_name[i] = nodei, nodei_name
|
|
else
|
|
local nodei_hardness = mover_hardness[nodei_name] or 1
|
|
if nodei_hardness < 596 then -- (3 * 99 diamond blocks + 1)
|
|
node[i], node_name[i] = nodei, nodei_name
|
|
hardness = hardness + nodei_hardness
|
|
else
|
|
maxpower = maxpower or minetest.get_meta( -- battery must be already connected
|
|
{x = meta:get_int("batx"), y = meta:get_int("baty"), z = meta:get_int("batz")}):get_float("maxpower")
|
|
if nodei_hardness > maxpower then -- ignore nodes too hard to move for the battery current upgrade
|
|
pos[i] = {}; count = count + 1
|
|
else
|
|
node[i], node_name[i] = nodei, nodei_name
|
|
hardness = hardness + nodei_hardness
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
if count == length_pos then -- only air/ignore/hard nodes, nothing to move
|
|
node_name = "air"
|
|
end
|
|
return false, pos, node, node_name, hardness
|
|
end
|
|
|
|
local function is_pos2_protected(pos, owner, mode_third_upgradetype)
|
|
if mode_third_upgradetype then
|
|
local length_pos = #pos
|
|
if length_pos > 0 then
|
|
local is_protected = minetest.is_protected
|
|
for i = 1, length_pos do
|
|
if is_protected(pos[i], owner) then
|
|
return true
|
|
end
|
|
end
|
|
return false, length_pos
|
|
end
|
|
end
|
|
return minetest.is_protected(pos, owner) -- protection check
|
|
end
|
|
|
|
minetest.register_node("basic_machines:mover", {
|
|
description = S("Mover"),
|
|
groups = {cracky = 2},
|
|
tiles = {"basic_machines_mover.png"},
|
|
sounds = basic_machines.sound_node_machine(),
|
|
|
|
after_place_node = function(pos, placer)
|
|
if not placer then return end
|
|
|
|
local meta, name = minetest.get_meta(pos), placer:get_player_name()
|
|
meta:set_string("infotext", S("Mover block. Set it up by punching or right click. Activated by signal."))
|
|
meta:set_string("owner", name)
|
|
|
|
meta:set_int("x0", 0); meta:set_int("y0", -1); meta:set_int("z0", 0) -- source1
|
|
meta:set_int("x1", 0); meta:set_int("y1", -1); meta:set_int("z1", 0) -- source2
|
|
meta:set_int("x2", 0); meta:set_int("y2", 1); meta:set_int("z2", 0) -- target
|
|
meta:set_int("pc", 0); meta:set_int("dim", 1) -- current cube position and dimensions
|
|
meta:set_float("fuel", 0)
|
|
meta:set_string("prefer", "")
|
|
meta:set_string("mode", "normal")
|
|
meta:set_int("upgradetype", 0); meta:set_int("upgrade", 1)
|
|
meta:set_int("seltab", 1) -- 0: undefined, 1: mode tab, 2: positions tab
|
|
meta:set_int("t", 0); meta:set_int("T", 0); meta:set_int("activation_count", 0)
|
|
|
|
basic_machines.find_and_connect_battery(pos, meta) -- try to find battery early
|
|
if minetest.check_player_privs(name, "privs") then
|
|
meta:set_int("upgrade", -1) -- means operations will be for free
|
|
end
|
|
|
|
local inv = meta:get_inventory()
|
|
inv:set_size("filter", 1)
|
|
inv:set_size("upgrade", 1)
|
|
|
|
local player_meta = placer:get_meta()
|
|
local mover_intro = player_meta:get_int("basic_machines:mover_intro")
|
|
if mover_intro < 2 then
|
|
if mover_intro == 0 then
|
|
player_meta:set_int("basic_machines:mover_intro", 2)
|
|
end
|
|
|
|
minetest.show_formspec(name, "basic_machines:intro_mover", "formspec_version[4]size[7.4,7.4]textarea[0,0.35;7.4,7.05;intro_mover;" ..
|
|
F(S("Mover introduction")) .. ";" .. F(S("This machine can move anything. General idea is the following:\n\n" ..
|
|
"First you need to define rectangle box work area (larger area, where it takes from, defined by source1/source2 which appear as two number 1 boxes) and target position (where it puts, marked by one number 2 box) by punching mover then following chat instructions exactly." ..
|
|
"\n\nCheck why it doesn't work: 1. did you click OK in mover after changing setting 2. does it have battery, 3. does battery have enough fuel 4. did you set filter for taking out of chest ?" ..
|
|
"\n\nImportant: Please read the help button inside machine before first use.")) .. "]")
|
|
end
|
|
end,
|
|
|
|
on_rightclick = function(pos, _, player)
|
|
local name, meta = player:get_player_name(), minetest.get_meta(pos)
|
|
|
|
machines.mark_pos1(name, vector_add(pos,
|
|
{x = meta:get_int("x0"), y = meta:get_int("y0"), z = meta:get_int("z0")})) -- mark pos1
|
|
machines.mark_pos11(name, vector_add(pos,
|
|
{x = meta:get_int("x1"), y = meta:get_int("y1"), z = meta:get_int("z1")})) -- mark pos11
|
|
machines.mark_pos2(name, vector_add(pos,
|
|
{x = meta:get_int("x2"), y = meta:get_int("y2"), z = meta:get_int("z2")})) -- mark pos2
|
|
|
|
minetest.show_formspec(name, "basic_machines:mover_" .. minetest.pos_to_string(pos),
|
|
basic_machines.get_mover_form(pos))
|
|
end,
|
|
|
|
on_dig = function(pos, node, digger)
|
|
if digger and digger:is_player() then
|
|
local meta = minetest.get_meta(pos)
|
|
if meta:get_string("owner") == digger:get_player_name() then -- owner can always remove his mover
|
|
local inv = meta:get_inventory()
|
|
local inv_stack
|
|
if not inv:is_empty("upgrade") then
|
|
inv_stack = inv:get_stack("upgrade", 1)
|
|
end
|
|
local node_removed = minetest.remove_node(pos)
|
|
if node_removed then
|
|
if inv_stack then
|
|
minetest.add_item(pos, inv_stack)
|
|
end
|
|
minetest.handle_node_drops(pos, {node.name}, digger)
|
|
end
|
|
return node_removed
|
|
end
|
|
end
|
|
return false
|
|
end,
|
|
|
|
allow_metadata_inventory_move = function()
|
|
return 0 -- no internal inventory moves!
|
|
end,
|
|
|
|
allow_metadata_inventory_put = function(pos, listname, _, stack, player)
|
|
local name = player:get_player_name()
|
|
if minetest.is_protected(pos, name) then return 0 end
|
|
local meta = minetest.get_meta(pos)
|
|
|
|
if listname == "filter" then
|
|
local item = stack:to_table(); if not item then return 0 end
|
|
local inv = meta:get_inventory()
|
|
local palette_index = tonumber(stack:get_meta():get("palette_index"))
|
|
|
|
local inv_stack = inv:get_stack("filter", 1)
|
|
if inv_stack:get_name() == item.name then
|
|
local inv_palette_index = tonumber(inv_stack:get_meta():get("palette_index"))
|
|
if inv_palette_index == palette_index then
|
|
item.count = inv_stack:get_count() + item.count
|
|
end
|
|
end
|
|
|
|
local mode = meta:get_string("mode")
|
|
local prefer = item.name .. (item.count > 1 and (" " .. math_min(item.count, 65535)) or "")
|
|
|
|
if basic_machines.check_mover_filter(mode, pos, meta, prefer) then -- input validation
|
|
if mover_no_large_stacks and basic_machines.check_mover_target(mode, pos, meta) then
|
|
prefer = basic_machines.clamp_item_count(prefer)
|
|
end
|
|
meta:set_string("prefer", prefer)
|
|
local filter_stack = basic_machines.itemstring_to_stack(prefer, palette_index)
|
|
inv:set_stack("filter", 1, filter_stack)
|
|
else
|
|
minetest.chat_send_player(name, S("MOVER: Wrong filter - must be the name of an existing block")); return 0
|
|
end
|
|
minetest.show_formspec(name, "basic_machines:mover_" .. minetest.pos_to_string(pos),
|
|
basic_machines.get_mover_form(pos))
|
|
elseif listname == "upgrade" then
|
|
local stack_name = stack:get_name()
|
|
local mover_upgrade = mover_upgrades[stack_name]
|
|
if mover_upgrade then
|
|
local inv_stack = meta:get_inventory():get_stack("upgrade", 1)
|
|
local inv_stack_is_empty = inv_stack:is_empty()
|
|
if inv_stack_is_empty or stack_name == inv_stack:get_name() then
|
|
local upgrade = inv_stack:get_count()
|
|
local upgrade_max = mover_upgrade.max
|
|
if upgrade < upgrade_max then
|
|
local stack_count = stack:get_count()
|
|
local new_upgrade = upgrade + stack_count
|
|
if new_upgrade > upgrade_max then
|
|
new_upgrade = upgrade_max -- not more than max
|
|
stack_count = math_min(stack_count, upgrade_max - upgrade)
|
|
end
|
|
if inv_stack_is_empty then meta:set_int("upgradetype", mover_upgrade.id) end
|
|
meta:set_int("upgrade", new_upgrade + 1)
|
|
return stack_count
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
return 0
|
|
end,
|
|
|
|
allow_metadata_inventory_take = function(pos, listname, _, stack, player)
|
|
local name = player:get_player_name()
|
|
if minetest.is_protected(pos, name) then return 0 end
|
|
local meta = minetest.get_meta(pos)
|
|
|
|
if listname == "filter" then
|
|
local inv = meta:get_inventory()
|
|
local inv_stack = inv:get_stack("filter", 1)
|
|
local count = inv_stack:get_count() - stack:get_count()
|
|
|
|
if count < 1 then
|
|
meta:set_string("prefer", "")
|
|
inv:set_stack("filter", 1, ItemStack(""))
|
|
-- inv:set_list("filter", {}) -- using saved map, mover with prefer previously set, it crashes the game... but why
|
|
else
|
|
local prefer = stack:get_name() .. (count > 1 and (" " .. count) or "")
|
|
meta:set_string("prefer", prefer)
|
|
local filter_stack = basic_machines.itemstring_to_stack(prefer, tonumber(inv_stack:get_meta():get("palette_index")))
|
|
inv:set_stack("filter", 1, filter_stack)
|
|
end
|
|
minetest.show_formspec(name, "basic_machines:mover_" .. minetest.pos_to_string(pos),
|
|
basic_machines.get_mover_form(pos))
|
|
return 0
|
|
elseif listname == "upgrade" then
|
|
if minetest.check_player_privs(name, "privs") then
|
|
meta:set_int("upgrade", -1) -- means operations will be for free
|
|
else
|
|
local stack_name = stack:get_name()
|
|
local mover_upgrade = mover_upgrades[stack_name]
|
|
if mover_upgrade then
|
|
local inv_stack = meta:get_inventory():get_stack("upgrade", 1)
|
|
if stack_name == inv_stack:get_name() then
|
|
local upgrade = inv_stack:get_count()
|
|
upgrade = upgrade - stack:get_count()
|
|
if upgrade < 0 or upgrade > mover_upgrade.max then upgrade = 0 end -- not less than 0 and not more than max
|
|
if upgrade == 0 then meta:set_int("upgradetype", 0) end
|
|
meta:set_int("upgrade", upgrade + 1)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
return stack:get_count()
|
|
end,
|
|
|
|
effector = {
|
|
action_on = function(pos, _)
|
|
local meta = minetest.get_meta(pos)
|
|
local upgradetype = meta:get_int("upgradetype")
|
|
local third_upgradetype = upgradetype == 3
|
|
local msg
|
|
|
|
-- temperature
|
|
local t0, t1 = meta:get_int("t"), minetest.get_gametime()
|
|
local tn, T = t1 - machines_minstep, meta:get_int("T") -- temperature
|
|
|
|
if t0 <= tn and T < mover_max_temp then
|
|
T = 0
|
|
end
|
|
|
|
if t0 > tn then -- activated before natural time
|
|
T = T + 1
|
|
elseif T > mover_max_temp or third_upgradetype and T > 0 then
|
|
if t1 - t0 > machines_timer then -- reset temperature if more than 5s (by default) elapsed since last activation
|
|
T = 0; msg = ""
|
|
else
|
|
T = T - 1
|
|
end
|
|
end
|
|
meta:set_int("t", t1); meta:set_int("T", T)
|
|
|
|
if T > mover_max_temp or third_upgradetype and T > 2 then
|
|
minetest.sound_play(basic_machines.sound_overheat, {pos = pos, max_hear_distance = 16, gain = 0.25}, true)
|
|
meta:set_string("infotext", S("Overheat! Temperature: @1", T))
|
|
return
|
|
end
|
|
|
|
-- variables
|
|
local mode = meta:get_string("mode")
|
|
local object = mode == "object"
|
|
local mreverse = meta:get_int("reverse")
|
|
local mode_third_upgradetype = third_upgradetype and (mode == "normal" or mode == "dig")
|
|
local owner = meta:get_string("owner")
|
|
local upgrade, prefer, source_chest
|
|
|
|
-- positions
|
|
local pos1 -- where to take from
|
|
local pos2 -- where to put
|
|
|
|
if object then
|
|
if meta:get_int("dim") ~= -1 then
|
|
meta:set_string("infotext", S("MOVER: Must reconfigure sources position.")); return
|
|
end
|
|
if mreverse == 1 then -- reverse pos1, pos2
|
|
pos1 = vector_add(pos, {x = meta:get_int("x2"), y = meta:get_int("y2"), z = meta:get_int("z2")})
|
|
pos2 = vector_add(pos, {x = meta:get_int("x0"), y = meta:get_int("y0"), z = meta:get_int("z0")})
|
|
else
|
|
pos1 = vector_add(pos, {x = meta:get_int("x0"), y = meta:get_int("y0"), z = meta:get_int("z0")}) -- source1
|
|
pos2 = vector_add(pos, {x = meta:get_int("x2"), y = meta:get_int("y2"), z = meta:get_int("z2")}) -- target
|
|
end
|
|
else
|
|
if meta:get_int("dim") < 1 then
|
|
meta:set_string("infotext", S("MOVER: Must reconfigure sources position.")); return
|
|
end
|
|
if mode_third_upgradetype then
|
|
pos1 = {}
|
|
|
|
local x0, y0, z0 = meta:get_int("x0"), meta:get_int("y0"), meta:get_int("z0") -- source1
|
|
local x1, y1 = meta:get_int("x1") - x0 + 1, meta:get_int("y1") - y0 + 1 -- get dimensions
|
|
local pc, dim = meta:get_int("pc"), meta:get_int("dim")
|
|
|
|
upgrade = meta:get_int("upgrade")
|
|
for i = 1, (upgrade == -1 and 1000 or upgrade) do -- up to 1000 blocks for admin
|
|
pc = (pc + 1) % dim
|
|
local yc = y0 + (pc % y1); local xpc = (pc - (pc % y1)) / y1
|
|
local xc = x0 + (xpc % x1)
|
|
local zc = z0 + (xpc - (xpc % x1)) / x1
|
|
pos1[i] = vector_add(pos, {x = xc, y = yc, z = zc})
|
|
if i >= dim then break end
|
|
end
|
|
meta:set_int("pc", pc) -- cycle position
|
|
|
|
local markerN = machines.markerN[owner]
|
|
if markerN then
|
|
local lua_entity = markerN:get_luaentity()
|
|
if lua_entity and vector.equals(pos, lua_entity._origin or {}) then
|
|
markerN:set_pos(pos1[#pos1]) -- mark current position
|
|
end
|
|
end
|
|
|
|
pos2 = vector_add(pos, {x = meta:get_int("x2"), y = meta:get_int("y2"), z = meta:get_int("z2")}) -- target
|
|
|
|
if mreverse == 1 then -- reverse pos1, pos2
|
|
local xt, yt, zt = pos2.x, pos2.y, pos2.z
|
|
pos2 = table.copy(pos1)
|
|
pos1 = {x = xt, y = yt, z = zt}
|
|
end
|
|
else
|
|
local x0, y0, z0 = meta:get_int("x0"), meta:get_int("y0"), meta:get_int("z0") -- source1
|
|
local x1, y1 = meta:get_int("x1") - x0 + 1, meta:get_int("y1") - y0 + 1 -- get dimensions
|
|
local pc = meta:get_int("pc"); pc = (pc + 1) % meta:get_int("dim"); meta:set_int("pc", pc) -- cycle position
|
|
-- pc = z * a * b + x * b + y, from x, y, z to pc
|
|
-- set current input position
|
|
local yc = y0 + (pc % y1); pc = (pc - (pc % y1)) / y1
|
|
local xc = x0 + (pc % x1); pc = (pc - (pc % x1)) / x1
|
|
local zc = z0 + pc
|
|
pos1 = vector_add(pos, {x = xc, y = yc, z = zc})
|
|
|
|
local markerN = machines.markerN[owner]
|
|
if markerN and T < temp_15P then
|
|
local lua_entity = markerN:get_luaentity()
|
|
if lua_entity and vector.equals(pos, lua_entity._origin or {}) then
|
|
markerN:set_pos(pos1) -- mark current position
|
|
end
|
|
end
|
|
|
|
local x2, y2, z2 = meta:get_int("x2"), meta:get_int("y2"), meta:get_int("z2") -- target
|
|
-- special mode that use its own source/target positions:
|
|
if mode == "transport" and mreverse < 2 then
|
|
pos2 = vector_add(pos1, {x = x2 - x0, y = y2 - y0, z = z2 - z0}) -- translation from pos1
|
|
else
|
|
pos2 = vector_add(pos, {x = x2, y = y2, z = z2})
|
|
end
|
|
|
|
if mreverse ~= 0 and mreverse ~= 2 then -- reverse pos1, pos2
|
|
local xt, yt, zt = pos1.x, pos1.y, pos1.z
|
|
pos1 = {x = pos2.x, y = pos2.y, z = pos2.z}
|
|
pos2 = {x = xt, y = yt, z = zt}
|
|
end
|
|
end
|
|
end
|
|
|
|
-- check pos1
|
|
upgrade = upgrade or meta:get_int("upgrade")
|
|
local first_pos1, pos_protected, node1, node1_name, nodes1_hardness
|
|
if mode_third_upgradetype then
|
|
local length_pos1 = #pos1
|
|
if length_pos1 > 0 then
|
|
first_pos1 = pos1[1]
|
|
pos_protected, pos1, node1, node1_name, nodes1_hardness = pos1list_checks(pos1, length_pos1, owner, upgrade, meta)
|
|
else
|
|
pos_protected, node1, node1_name = pos1_checks(pos1, owner)
|
|
end
|
|
else
|
|
pos_protected, node1, node1_name = pos1_checks(pos1, owner)
|
|
end
|
|
|
|
if pos_protected then -- protection check
|
|
meta:set_int("T", T + math.ceil(mover_max_temp * 0.2))
|
|
meta:set_string("infotext", S("Mover block. Protection fail.")); return
|
|
elseif not object and node1_name == "air" or node1_name == "ignore" then -- node check
|
|
return -- nothing to move
|
|
end
|
|
|
|
-- check pos2
|
|
local length_pos2
|
|
pos_protected, length_pos2 = is_pos2_protected(pos2, owner, mode_third_upgradetype)
|
|
if pos_protected then -- protection check
|
|
meta:set_int("T", T + math.ceil(mover_max_temp * 0.2))
|
|
meta:set_string("infotext", S("Mover block. Protection fail.")); return
|
|
end
|
|
|
|
-- fuel
|
|
local fuel_cost
|
|
local fuel = meta:get_float("fuel")
|
|
|
|
if upgrade == -1 then
|
|
fuel_cost = 0 -- free operations for admin
|
|
else -- calculate fuel cost
|
|
if object then
|
|
if meta:get_int("elevator") == 1 then -- check if elevator mode
|
|
local requirement = basic_machines.calculate_elevator_requirement(get_distance(pos, pos2))
|
|
if (upgrade - 1) >= requirement and (meta:get_int("upgradetype") == 2 or
|
|
meta:get_inventory():get_stack("upgrade", 1):get_name() == "default:diamondblock") -- for compatibility
|
|
then
|
|
fuel_cost = 0
|
|
else
|
|
local upgrade_item = mover_revupgrades[2]
|
|
local description = basic_machines.get_item_description(upgrade_item)
|
|
meta:set_string("infotext",
|
|
S("MOVER: Elevator error. Need at least @1 of '@2' (@3) in upgrade (1 for every @4 distance).",
|
|
requirement, description, upgrade_item, basic_machines.elevator_height)); return
|
|
end
|
|
else
|
|
local hardness = mover.hardness[node1_name]
|
|
if hardness == 0 then hardness = 1 end -- no free teleport from machine blocks
|
|
fuel_cost = hardness or 1
|
|
end
|
|
elseif mode == "inventory" then -- taking items from chests/inventory move
|
|
prefer = meta:get_string("prefer")
|
|
fuel_cost = mover.hardness[prefer] or 1
|
|
else
|
|
source_chest = mover.chests[node1_name]
|
|
if source_chest then
|
|
prefer = meta:get_string("prefer")
|
|
fuel_cost = mover.hardness[prefer] or 1
|
|
if node1_name == "default:chest" and fuel_cost > 1 then
|
|
fuel_cost = fuel_cost * 0.65
|
|
end
|
|
if mode_third_upgradetype then
|
|
fuel_cost = fuel_cost * length_pos2
|
|
end
|
|
else
|
|
fuel_cost = nodes1_hardness or mover.hardness[node1_name] or 1 -- add maxpower battery check too ?
|
|
end
|
|
end
|
|
|
|
if fuel_cost > 0 then
|
|
local dist
|
|
if mode_third_upgradetype then
|
|
if length_pos2 then
|
|
dist = get_distance(pos1, pos2[length_pos2])
|
|
else
|
|
dist = get_distance(first_pos1, pos2)
|
|
end
|
|
else
|
|
dist = get_distance(pos1, pos2)
|
|
end
|
|
-- machines_operations = 10 by default, so 10 basic operations possible with 1 coal
|
|
fuel_cost = fuel_cost * dist / machines_operations
|
|
|
|
if mode == "inventory" or object then
|
|
fuel_cost = fuel_cost * 0.1
|
|
elseif mode == "dig" and not third_upgradetype then
|
|
fuel_cost = fuel_cost * 1.1
|
|
end
|
|
|
|
if temp_80P and not third_upgradetype then
|
|
if T > temp_80P then
|
|
fuel_cost = fuel_cost + (0.2 / mover_max_temp) * T * fuel_cost
|
|
elseif T < temp_15P then
|
|
fuel_cost = fuel_cost * 0.97
|
|
end
|
|
end
|
|
|
|
if upgradetype == 1 or third_upgradetype or
|
|
meta:get_inventory():get_stack("upgrade", 1):get_name() == "default:mese" -- for compatibility
|
|
then
|
|
fuel_cost = fuel_cost / math_min(mover_upgrade_max + 1, upgrade) -- upgrade decreases fuel cost
|
|
end
|
|
end
|
|
|
|
if fuel < fuel_cost then -- fuel operations: needs fuel to operate, find nearby battery
|
|
local power_draw = fuel_cost; local supply
|
|
if power_draw < 1 then power_draw = 1 end -- at least 10 one block operations with 1 refuel
|
|
if power_draw == 1 then
|
|
local bpos = {x = meta:get_int("batx"), y = meta:get_int("baty"), z = meta:get_int("batz")} -- battery pos
|
|
supply = basic_machines.check_power(bpos, power_draw * 3) -- try to store energy to reduce refuel
|
|
if supply <= 0 then
|
|
supply = basic_machines.check_power(bpos, power_draw)
|
|
end
|
|
else
|
|
supply = basic_machines.check_power(
|
|
{x = meta:get_int("batx"), y = meta:get_int("baty"), z = meta:get_int("batz")}, power_draw)
|
|
end
|
|
|
|
if supply > 0 then -- fuel found
|
|
fuel = fuel + supply
|
|
elseif supply < 0 then -- no battery at target location, try to find it!
|
|
if not basic_machines.find_and_connect_battery(pos, meta) then
|
|
meta:set_string("infotext", S("Can not find nearby battery to connect to!"))
|
|
minetest.sound_play(basic_machines.sound_overheat, {pos = pos, gain = 1, max_hear_distance = 8}, true)
|
|
return
|
|
end
|
|
end
|
|
|
|
if fuel < fuel_cost then
|
|
meta:set_float("fuel", fuel)
|
|
meta:set_string("infotext", S("Mover block. Energy @1, needed energy @2. Put nonempty battery next to mover.",
|
|
twodigits_float(fuel), twodigits_float(fuel_cost))); return
|
|
else
|
|
msg = S("Mover block refueled. Fuel status @1.", twodigits_float(fuel))
|
|
end
|
|
end
|
|
end
|
|
|
|
-- do the thing
|
|
local activation_count, new_fuel_cost = (mover_modes[mode] or {}).task(pos, meta, owner, prefer, pos1, node1, node1_name, source_chest, pos2, mreverse, upgradetype, upgrade, fuel_cost)
|
|
|
|
if activation_count then -- something happened
|
|
if t0 > tn then
|
|
meta:set_int("activation_count", activation_count + 1)
|
|
elseif activation_count > 0 then
|
|
meta:set_int("activation_count", 0)
|
|
end
|
|
fuel_cost = new_fuel_cost or fuel_cost
|
|
if fuel_cost ~= 0 then
|
|
fuel = fuel - fuel_cost; meta:set_float("fuel", fuel) -- fuel remaining
|
|
end
|
|
meta:set_string("infotext", S("Mover block. Temperature: @1, Fuel: @2.", T, twodigits_float(fuel)))
|
|
elseif fuel_cost > 1.5 then
|
|
fuel = fuel - fuel_cost * 0.03; meta:set_float("fuel", fuel) -- 3% fuel cost if no task done
|
|
meta:set_string("infotext", S("Mover block. Temperature: @1, Fuel: @2.", T, twodigits_float(fuel)))
|
|
elseif msg then -- mover refueled
|
|
meta:set_float("fuel", fuel)
|
|
meta:set_string("infotext", msg)
|
|
end
|
|
end,
|
|
|
|
action_off = function(pos, _) -- this toggles reverse option of mover
|
|
local meta = minetest.get_meta(pos)
|
|
local mreverse = meta:get_int("reverse")
|
|
if mreverse == 1 then mreverse = 0 elseif mreverse == 0 then mreverse = 1 end
|
|
meta:set_int("reverse", mreverse)
|
|
end
|
|
}
|
|
})
|
|
|
|
if basic_machines.settings.register_crafts and basic_machines.use_default then
|
|
minetest.register_craft({
|
|
output = "basic_machines:mover",
|
|
recipe = {
|
|
{"default:mese_crystal", "default:mese_crystal", "default:mese_crystal"},
|
|
{"default:mese_crystal", "default:mese_crystal", "default:mese_crystal"},
|
|
{"default:stone", "basic_machines:keypad", "default:stone"}
|
|
}
|
|
})
|
|
end |