diff --git a/technic/helpers.lua b/technic/helpers.lua index 02f966b9..7252766c 100644 --- a/technic/helpers.lua +++ b/technic/helpers.lua @@ -47,13 +47,11 @@ assert(technic.pretty_num(0) == "0 ") assert(technic.pretty_num(1234) == "1234 ") assert(technic.pretty_num(123456789) == "123.5 M") - -- used to display power values function technic.EU_string(num) return technic.pretty_num(num) .. "EU" end - --- Same as minetest.swap_node, but only changes name -- and doesn't re-set if already set. function technic.swap_node(pos, name) @@ -64,19 +62,44 @@ function technic.swap_node(pos, name) end end +function technic.set_RE_charge(stack, charge) + local wear_factor = stack:get_definition().technic_wear_factor + if wear_factor then + local wear = math.floor(charge * wear_factor + 0.5) + stack:set_wear(wear > 0 and 65536 - wear or 0) + else + minetest.log("error", "technic.set_RE_charge item not registered as power tool: "..stack:get_name()) + end +end ---- Fully charge RE chargeable item. --- Must be defined early to reference in item definitions. -function technic.refill_RE_charge(stack) - local max_charge = technic.power_tools[stack:get_name()] - if not max_charge then return stack end - technic.set_RE_wear(stack, max_charge, max_charge) - local meta = minetest.deserialize(stack:get_metadata()) or {} - meta.charge = max_charge - stack:set_metadata(minetest.serialize(meta)) - return stack +function technic.get_RE_charge(stack) + local wear_factor = stack:get_definition().technic_wear_factor + if wear_factor then + local wear = stack:get_wear() + return math.floor((wear > 0 and 65536 - wear or 0) / wear_factor + 0.5) + end + minetest.log("warning", "technic.get_RE_charge item not registered as power tool: "..stack:get_name()) + return 0 end +function technic.use_RE_charge(stack, amount) + if technic.creative_mode or amount <= 0 then + -- Do not check charge in creative mode or when trying to use zero amount + return true + end + local charge = technic.get_RE_charge(stack) + if charge < amount then + -- Not enough energy available + return false + end + -- Ensure that at least single point is always added to wear when using tool + local wear_factor = stack:get_definition().technic_wear_factor + local wear = stack:get_wear() + wear = math.max(math.floor(wear + 1.5), math.floor(amount * wear_factor + wear + 0.5)) + stack:set_wear(wear > 65535 and 0 or wear) + -- Charge used successfully + return true +end -- If the node is loaded, returns it. If it isn't loaded, load it and return nil. function technic.get_or_load_node(pos) @@ -87,7 +110,6 @@ function technic.get_or_load_node(pos) return nil end - technic.tube_inject_item = pipeworks.tube_inject_item or function(pos, start_pos, velocity, item) local tubed = pipeworks.tube_item(vector.new(pos), item) tubed:get_luaentity().start_pos = vector.new(start_pos) @@ -95,7 +117,6 @@ technic.tube_inject_item = pipeworks.tube_inject_item or function(pos, start_pos tubed:set_acceleration(vector.new(0, 0, 0)) end - --- Iterates over the node positions along the specified ray. -- The returned positions will not include the starting position. function technic.trace_node_ray(pos, dir, range) @@ -143,7 +164,6 @@ function technic.trace_node_ray(pos, dir, range) end, vector.round(pos) end - --- Like trace_node_ray, but includes extra positions close to the ray. function technic.trace_node_ray_fat(pos, dir, range) local x_step = dir.x > 0 and 1 or -1 diff --git a/technic/init.lua b/technic/init.lua index 27c02006..2e797f3d 100644 --- a/technic/init.lua +++ b/technic/init.lua @@ -5,6 +5,7 @@ local load_start = os.clock() technic = rawget(_G, "technic") or {} +technic.plus = true technic.creative_mode = minetest.settings:get_bool("creative_mode") local modpath = minetest.get_modpath("technic") @@ -23,15 +24,18 @@ dofile(modpath.."/max_lag.lua") -- Helper functions dofile(modpath.."/helpers.lua") +-- Register functions +dofile(modpath.."/register.lua") + +-- Compatibility shims for tools +dofile(modpath.."/machines/compat/tools.lua") + -- Items dofile(modpath.."/items.lua") -- Craft recipes for items dofile(modpath.."/crafts.lua") --- Register functions -dofile(modpath.."/register.lua") - -- Radiation dofile(modpath.."/radiation.lua") diff --git a/technic/items.lua b/technic/items.lua index bcd8b1de..3a257034 100644 --- a/technic/items.lua +++ b/technic/items.lua @@ -21,55 +21,34 @@ minetest.register_craftitem( "technic:diamond_drill_head", { inventory_image = "technic_diamond_drill_head.png", }) -minetest.register_tool("technic:blue_energy_crystal", { +technic.register_power_tool("technic:blue_energy_crystal", { description = S("Blue Energy Crystal"), inventory_image = minetest.inventorycube( "technic_diamond_block_blue.png", "technic_diamond_block_blue.png", "technic_diamond_block_blue.png"), groups = { disable_repair = 1 }, - wear_represents = "technic_RE_charge", - on_refill = technic.refill_RE_charge, - tool_capabilities = { - max_drop_level = 0, - groupcaps = { - fleshy = {times={}, uses=10000, maxlevel=0} - } - } + max_charge = 450000, }) -minetest.register_tool("technic:green_energy_crystal", { +technic.register_power_tool("technic:green_energy_crystal", { description = S("Green Energy Crystal"), inventory_image = minetest.inventorycube( "technic_diamond_block_green.png", "technic_diamond_block_green.png", "technic_diamond_block_green.png"), groups = { disable_repair = 1 }, - wear_represents = "technic_RE_charge", - on_refill = technic.refill_RE_charge, - tool_capabilities = { - max_drop_level = 0, - groupcaps = { - fleshy = {times={}, uses=10000, maxlevel=0} - } - } + max_charge = 150000, }) -minetest.register_tool("technic:red_energy_crystal", { +technic.register_power_tool("technic:red_energy_crystal", { description = S("Red Energy Crystal"), inventory_image = minetest.inventorycube( "technic_diamond_block_red.png", "technic_diamond_block_red.png", "technic_diamond_block_red.png"), groups = { disable_repair = 1 }, - wear_represents = "technic_RE_charge", - on_refill = technic.refill_RE_charge, - tool_capabilities = { - max_drop_level = 0, - groupcaps = { - fleshy = {times={}, uses=10000, maxlevel=0} - } - } + max_charge = 50000, }) minetest.register_craftitem("technic:copper_coil", { diff --git a/technic/machines/compat/digtron.lua b/technic/machines/compat/digtron.lua index c5aeddd1..f7779128 100644 --- a/technic/machines/compat/digtron.lua +++ b/technic/machines/compat/digtron.lua @@ -1,9 +1,65 @@ -- --- Compatibility hacks for digtron to work well with technic plus new network system +-- Compatibility hacks for digtron to work well with new Technic Plus network and power tools -- -- More information: -- https://github.com/mt-mods/technic/issues/100 +-- https://github.com/mt-mods/technic/issues/233 -- +-- Disable some luacheck warnings to allow having original formatting here +-- luacheck: no max line length +-- luacheck: globals digtron + +-- Only relevant sections modified, you can directly compare this with upstream function defined in util.lua +local node_inventory_table = {type="node"} +local function tap_batteries(battery_positions, target, test) + if (battery_positions == nil) then + return 0 + end + + local current_burned = 0 + -- 1 coal block is 370 PU + -- 1 coal lump is 40 PU + -- An RE battery holds 10000 EU of charge + -- local power_ratio = 100 -- How much charge equals 1 unit of PU from coal + -- setting Moved to digtron.config.power_ratio + + for k, location in pairs(battery_positions) do + if current_burned > target then + break + end + node_inventory_table.pos = location.pos + local inv = minetest.get_inventory(node_inventory_table) + local invlist = inv:get_list("batteries") + + if (invlist == nil) then -- This check shouldn't be needed, it's yet another guard against https://github.com/minetest/minetest/issues/8067 + break + end + + for i, itemstack in pairs(invlist) do + local charge = technic.get_RE_charge(itemstack) + local power_available = math.floor(charge / digtron.config.power_ratio) + if power_available ~= 0 then + local actual_burned = power_available -- we just take all we have from the battery, since they aren't stackable + -- don't bother recording the items if we're just testing, nothing is actually being removed. + if test ~= true then + -- since we are taking everything, the wear and charge can both be set to 0 + technic.set_RE_charge(itemstack, 0) + end + current_burned = current_burned + actual_burned + end + + if current_burned > target then + break + end + end + + if test ~= true then + -- only update the list if we're doing this for real. + inv:set_list("batteries", invlist) + end + end + return current_burned +end local function power_connector_compat() local digtron_technic_run = minetest.registered_nodes["digtron:power_connector"].technic_run @@ -23,8 +79,31 @@ local function power_connector_compat() }) end +local function battery_holder_compat() + -- Override battery holder + local tube = minetest.registered_nodes["digtron:battery_holder"].tube + tube.can_insert = function(pos, node, stack, direction) + if technic.get_RE_charge(stack) > 0 then + local inv = minetest.get_meta(pos):get_inventory() + return inv:room_for_item("batteries", stack) + end + return false + end + minetest.override_item("digtron:battery_holder",{ + allow_metadata_inventory_put = function(pos, listname, index, stack, player) + return (listname == "batteries" and technic.get_RE_charge(stack) > 0) and stack:get_count() or 0 + end, + tube = tube, + }) + -- Override digtron.tap_batteries + digtron.tap_batteries = tap_batteries +end + minetest.register_on_mods_loaded(function() if minetest.registered_nodes["digtron:power_connector"] then power_connector_compat() end + if minetest.registered_nodes["digtron:battery_holder"] then + battery_holder_compat() + end end) diff --git a/technic/machines/compat/tools.lua b/technic/machines/compat/tools.lua new file mode 100644 index 00000000..2241e5b0 --- /dev/null +++ b/technic/machines/compat/tools.lua @@ -0,0 +1,40 @@ + +-- This wont give full compatibility but allows using old style technic.register_power_tool tool registration function. +-- Tools still have to read charge value using technic.get_RE_charge, allows easier compatibility with official Technic +-- mod for some tools: a lot less changes required for compatibility but tool will keep some unnecessary meatadata. +-- +-- To make tool fully compatible replace minetest.register_tool with technic.register_power_tool and add `max_charge` +-- field for tool definition. Fields `wear_represents` and `on_refill` can also be removed if using defaults. +-- +local register_power_tool = technic.register_power_tool +function technic.register_power_tool(itemname, itemdef) + if type(itemdef) == "number" then + minetest.log("warning", "Deprecated technic.register_power_tool use. Setting max_charge for "..itemname) + technic.power_tools[itemname] = itemdef + minetest.register_on_mods_loaded(function() + minetest.log("warning", "Deprecated technic.register_power_tool use. Ensuring fields for "..itemname) + local redef = minetest.registered_items[itemname] + if redef and redef.wear_represents == "technic_RE_charge" and not redef.on_refill then + -- Override power tools that called register_power_tool but do not have on_refill function defined + local max_charge = itemdef + minetest.override_item(itemname, { + on_refill = function(stack) + technic.set_RE_charge(stack, max_charge) + return stack + end, + technic_max_charge = max_charge, + technic_wear_factor = 65535 / max_charge, + }) + minetest.log("warning", "Updated on_refill and max_charge for "..itemname) + end + end) + else + return register_power_tool(itemname, itemdef) + end +end + +-- Alias set set_RE_wear, many tools calls this to set wear value which is also handled by set_RE_charge +function technic.set_RE_wear(stack, charge) + minetest.log("warning", "Use of deprecated function technic.set_RE_wear with stack: "..stack:get_name()) + technic.set_RE_charge(stack, charge) +end diff --git a/technic/machines/register/battery_box.lua b/technic/machines/register/battery_box.lua index 8ed14cdf..f82a9691 100644 --- a/technic/machines/register/battery_box.lua +++ b/technic/machines/register/battery_box.lua @@ -5,11 +5,6 @@ local S = technic.getter local tube_entry = "^pipeworks_tube_connection_metallic.png" local cable_entry = "^technic_cable_connection_overlay.png" -technic.register_power_tool("technic:battery", 10000) -technic.register_power_tool("technic:red_energy_crystal", 50000) -technic.register_power_tool("technic:green_energy_crystal", 150000) -technic.register_power_tool("technic:blue_energy_crystal", 450000) - -- Battery recipes: -- Tin-copper recipe: minetest.register_craft({ @@ -66,19 +61,10 @@ minetest.register_craft({ } }) -minetest.register_tool("technic:battery", { +technic.register_power_tool("technic:battery", { description = S("RE Battery"), inventory_image = "technic_battery.png", groups = { disable_repair = 1 }, - wear_represents = "technic_RE_charge", - on_refill = technic.refill_RE_charge, - tool_capabilities = { - charge = 0, - max_drop_level = 0, - groupcaps = { - fleshy = {times={}, uses=10000, maxlevel=0} - } - } }) -- x+2 + (z+2)*2 @@ -213,54 +199,53 @@ function technic.register_battery_box(nodename, data) return end -- Load and check tool metadata - local toolmeta = minetest.deserialize(toolstack:get_metadata()) or {} - if not toolmeta.charge then - toolmeta.charge = 0 - end - return toolstack, toolmeta, technic.power_tools[toolname] + return toolstack, technic.power_tools[toolname] end local function charge_tools(meta, batt_charge, charge_step) -- Get tool metadata local inv = meta:get_inventory() - local toolstack, toolmeta, max_charge = get_tool(inv, "src") - if not toolstack then return batt_charge, false end + local toolstack, max_charge = get_tool(inv, "src") + if not toolstack then + return batt_charge, false + end -- Do the charging - if toolmeta.charge >= max_charge then + local charge = technic.get_RE_charge(toolstack) + if charge >= max_charge then return batt_charge, true elseif batt_charge <= 0 then return batt_charge, false end - charge_step = math.min(charge_step, batt_charge) - charge_step = math.min(charge_step, max_charge - toolmeta.charge) - toolmeta.charge = toolmeta.charge + charge_step - technic.set_RE_wear(toolstack, toolmeta.charge, max_charge) - toolmeta.charge = toolmeta.charge - toolstack:set_metadata(minetest.serialize(toolmeta)) - inv:set_stack("src", 1, toolstack) - return batt_charge - charge_step, (toolmeta.charge == max_charge) + local oldcharge = charge + charge_step = math.min(math.min(charge_step, batt_charge), max_charge - charge) + charge = charge + charge_step + if charge ~= oldcharge then + technic.set_RE_charge(toolstack, charge) + inv:set_stack("src", 1, toolstack) + end + return batt_charge - charge_step, (charge == max_charge) end local function discharge_tools(meta, batt_charge, charge_step, batt_max_charge) -- Get tool metadata local inv = meta:get_inventory() - local toolstack, toolmeta, max_charge = get_tool(inv, "dst") + local toolstack = get_tool(inv, "dst") if not toolstack then return batt_charge, false end -- Do the discharging - local tool_charge = toolmeta.charge - if tool_charge <= 0 then + local charge = technic.get_RE_charge(toolstack) + if charge <= 0 then return batt_charge, true elseif batt_charge >= batt_max_charge then return batt_charge, false end - charge_step = math.min(charge_step, batt_max_charge - batt_charge) - charge_step = math.min(charge_step, tool_charge) - tool_charge = tool_charge - charge_step - technic.set_RE_wear(toolstack, tool_charge, max_charge) - toolmeta.charge = tool_charge - toolstack:set_metadata(minetest.serialize(toolmeta)) - inv:set_stack("dst", 1, toolstack) - return batt_charge + charge_step, (tool_charge == 0) + local oldcharge = charge + charge_step = math.min(math.min(charge_step, batt_max_charge - batt_charge), charge) + charge = charge - charge_step + if charge ~= oldcharge then + technic.set_RE_charge(toolstack, charge) + inv:set_stack("dst", 1, toolstack) + end + return batt_charge + charge_step, (charge == 0) end local function run(pos, node, run_state, network) diff --git a/technic/register.lua b/technic/register.lua index cc60cd56..76af51a4 100644 --- a/technic/register.lua +++ b/technic/register.lua @@ -28,40 +28,18 @@ function technic.register_machine(tier, nodename, machine_type) table.insert(technic.machine_tiers[nodename], tier) end -function technic.register_power_tool(craftitem, max_charge) - technic.power_tools[craftitem] = max_charge -end - - --- Utility functions. Not sure exactly what they do.. water.lua uses the two first. -function technic.get_RE_item_load(load1, max_load) - if load1 == 0 then load1 = 65535 end - local temp = 65536 - load1 - temp = temp / 65535 * max_load - return math.floor(temp + 0.5) -end - -function technic.set_RE_item_load(load1, max_load) - if load1 == 0 then return 65535 end - local temp = load1 / max_load * 65535 - temp = 65536 - temp - return math.floor(temp) -end - --- Wear down a tool depending on the remaining charge. -function technic.set_RE_wear(itemstack, item_load, max_load) - if (minetest.registered_items[itemstack:get_name()].wear_represents - or "mechanical_wear") ~= "technic_RE_charge" then - return itemstack - end - local temp - if item_load == 0 then - temp = 0 - else - temp = 65536 - math.floor(item_load / max_load * 65535) - if temp > 65535 then temp = 65535 end - if temp < 1 then temp = 1 end +function technic.register_power_tool(itemname, itemdef) + local max_charge = itemdef.technic_max_charge or itemdef.max_charge or 10000 + itemdef.max_charge = nil + itemdef.wear_represents = itemdef.wear_represents or "technic_RE_charge" + itemdef.technic_max_charge = max_charge + itemdef.technic_wear_factor = 65535 / max_charge + itemdef.on_refill = itemdef.on_refill or function(stack) + -- This is always using originally defined max_charge even if stack somehow changed to another + technic.set_RE_charge(stack, max_charge) + return stack end - itemstack:set_wear(temp) - return itemstack + itemdef.tool_capabilities = itemdef.tool_capabilities or { punch_attack_uses = 0 } + minetest.register_tool(itemname, itemdef) + technic.power_tools[itemname] = max_charge end diff --git a/technic/spec/hv_network_spec.lua b/technic/spec/hv_network_spec.lua index b226cbc4..46ddf662 100644 --- a/technic/spec/hv_network_spec.lua +++ b/technic/spec/hv_network_spec.lua @@ -16,6 +16,7 @@ describe("HV machine network", function() mineunit:mods_loaded() -- Tell mods that 1 minute passed already to execute all weird minetest.after hacks. mineunit:execute_globalstep(60) + world.set_default_node("air") local machines = { "technic:hv_generator", diff --git a/technic/spec/lv_network_spec.lua b/technic/spec/lv_network_spec.lua index d75ab06b..39097932 100644 --- a/technic/spec/lv_network_spec.lua +++ b/technic/spec/lv_network_spec.lua @@ -16,6 +16,7 @@ describe("LV machine network", function() mineunit:mods_loaded() -- Tell mods that 1 minute passed already to execute all weird minetest.after hacks. mineunit:execute_globalstep(60) + world.set_default_node("air") local machines = { "technic:lv_generator", diff --git a/technic/spec/network_spec.lua b/technic/spec/network_spec.lua index 027ece0b..dcba6efc 100644 --- a/technic/spec/network_spec.lua +++ b/technic/spec/network_spec.lua @@ -12,6 +12,8 @@ sourcefile("machines/HV/generator") describe("Power network helper", function() + world.set_default_node("air") + world.layout({ {{x=100,y=100,z=100}, "technic:lv_cable"}, {{x=101,y=100,z=100}, "technic:lv_cable"}, diff --git a/technic/spec/tools_spec.lua b/technic/spec/tools_spec.lua new file mode 100644 index 00000000..90ed7a80 --- /dev/null +++ b/technic/spec/tools_spec.lua @@ -0,0 +1,453 @@ +require("mineunit") +--[[ + Technic tool regression tests. + Execute mineunit at technic source directory. +--]] + +-- Load complete technic mod +fixture("technic") +sourcefile("init") + +local RUN_TECHNIC_ADDONS_CHAINSAWMK3_TESTS = false + +describe("Technic power tool", function() + + if RUN_TECHNIC_ADDONS_CHAINSAWMK3_TESTS then + -- Load technic_addons mod + mineunit:set_modpath("technic_addons", "../../technic_addons") + mineunit:set_current_modname("technic_addons") + sourcefile("../../technic_addons/init") + mineunit:restore_current_modname() + end + + world.set_default_node("air") + + -- HV battery box and some HV solar arrays for charging + local BB_Charge_POS = {x=0,y=51,z=0} + local BB_Discharge_POS = {x=0,y=51,z=2} + world.layout({ + -- Network with generators for charging tools in battery box + {BB_Charge_POS, "technic:hv_battery_box0"}, + {{x=1,y=51,z=0}, "technic:switching_station"}, + {{{x=2,y=51,z=0},{x=10,y=51,z=0}}, "technic:solar_array_hv"}, + {{{x=0,y=50,z=0},{x=10,y=50,z=0}}, "technic:hv_cable"}, + -- Network without generators for discharging tools in battery box + {BB_Discharge_POS, "technic:hv_battery_box0"}, + {{x=1,y=51,z=2}, "technic:switching_station"}, + {{{x=0,y=50,z=2},{x=1,y=50,z=2}}, "technic:hv_cable"}, + }) + + -- Some helpers to make stack access simpler + local player = Player("SX") + local charge_inv = minetest.get_meta(BB_Charge_POS):get_inventory() + local discharge_inv = minetest.get_meta(BB_Discharge_POS):get_inventory() + local function set_charge_stack(stack) charge_inv:set_stack("src", 1, stack) end + local function get_charge_stack() return charge_inv:get_stack("src", 1) end + local function set_discharge_stack(stack) discharge_inv:set_stack("src", 1, stack) end + local function get_discharge_stack() return discharge_inv:get_stack("src", 1) end + local function set_player_stack(stack) return player:get_inventory():set_stack("main", 1, stack) end + local function get_player_stack() return player:get_inventory():get_stack("main", 1) end + + -- Execute on mods loaded callbacks to finish loading. + mineunit:mods_loaded() + -- Tell mods that 1 minute passed already to execute all weird minetest.after hacks. + mineunit:execute_globalstep(60) + + describe("API", function() + + setup(function() + set_charge_stack(ItemStack()) + set_discharge_stack(ItemStack()) + mineunit:set_current_modname("mymod") + mineunit:execute_on_joinplayer(player) + end) + + teardown(function() + mineunit:execute_on_leaveplayer(player) + mineunit:restore_current_modname() + end) + + local use_RE_charge_result + + it("technic.register_power_tool works", function() + technic.register_power_tool("mymod:powertool", { + description = "My Mod Power Tool", + inventory_image = "mymod_powertool.png", + max_charge = 1234, + on_use = function(itemstack, player, pointed_thing) + use_RE_charge_result = technic.use_RE_charge(itemstack, 123) + return itemstack + end, + }) + local itemdef = minetest.registered_items["mymod:powertool"] + assert.is_hashed(itemdef) + assert.is_function(itemdef.on_use) + assert.is_function(itemdef.on_refill) + assert.equals("technic_RE_charge", itemdef.wear_represents) + assert.is_number(itemdef.technic_max_charge) + assert.gt(itemdef.technic_max_charge, 0) + end) + + it("technic.use_RE_charge works (zero charge)", function() + set_player_stack("mymod:powertool") + spy.on(technic, "use_RE_charge") + player:do_use(player:get_pos()) + assert.spy(technic.use_RE_charge).called(1) + assert.equals("boolean", type(use_RE_charge_result)) + assert.is_false(use_RE_charge_result) + end) + + it("technic.get_RE_charge works (zero charge)", function() + assert.equals(0, technic.get_RE_charge(ItemStack("mymod:powertool"))) + end) + + it("technic.set_RE_charge works (zero charge -> 123)", function() + local stack = ItemStack("mymod:powertool") + technic.set_RE_charge(stack, 123) + assert.equals(123, technic.get_RE_charge(stack)) + end) + + it("technic.use_RE_charge works (minimum charge)", function() + -- Add partially charged tool to player inventory + local stack = ItemStack("mymod:powertool") + technic.set_RE_charge(stack, 123) + set_player_stack(stack) + + -- Use tool and verify results + spy.on(technic, "use_RE_charge") + player:do_use(player:get_pos()) + assert.spy(technic.use_RE_charge).called(1) + assert.equals("boolean", type(use_RE_charge_result)) + assert.is_true(use_RE_charge_result) + assert.equals(0, technic.get_RE_charge(get_player_stack())) + end) + + it("technic.use_RE_charge works (minimum charge + 1)", function() + -- Add partially charged tool to player inventory + local stack = ItemStack("mymod:powertool") + technic.set_RE_charge(stack, 124) + set_player_stack(stack) + -- Use tool and verify results + player:do_use(player:get_pos()) + assert.equals(1, technic.get_RE_charge(get_player_stack())) + end) + + end) + + describe("charging and discharging extreme ratios", function() + + local function register_test_tool(name, charge_per_use, max_charge) + technic.register_power_tool("mymod:"..name, { + description = name, + inventory_image = "mymod_powertool.png", + max_charge = max_charge, + on_use = function(itemstack, player, pointed_thing) + technic.use_RE_charge(itemstack, charge_per_use) + return itemstack + end, + }) + end + + setup(function() + set_charge_stack(ItemStack()) + set_discharge_stack(ItemStack()) + mineunit:set_current_modname("mymod") + register_test_tool("t1_1", 1, 1) + register_test_tool("t1_2", 1, 2) + register_test_tool("t1_10001", 1, 10001) + register_test_tool("t1_65535", 1, 65535) + register_test_tool("t1_65536", 1, 65536) + register_test_tool("t100_6553500", 1, 6553500) + register_test_tool("t100_6553600", 1, 6553600) + register_test_tool("t2_3", 2, 3) + mineunit:restore_current_modname() + mineunit:execute_on_joinplayer(player) + end) + + teardown(function() + mineunit:execute_on_leaveplayer(player) + end) + + -- Very basic tests for few simple and straightforward max_charge values + -- Add tool to battery box and test charging, 10kEU / cycle + + it("t1_1 can be charged", function() + set_charge_stack(ItemStack("mymod:t1_1")) + assert.equals(0, technic.get_RE_charge(get_charge_stack())) + mineunit:execute_globalstep(1) + assert.equals(1, technic.get_RE_charge(get_charge_stack())) + end) + + it("t1_2 can be charged", function() + -- Add tool to battery box + set_charge_stack(ItemStack("mymod:t1_2")) + -- Test charging, 10kEU / cycle + assert.equals(0, technic.get_RE_charge(get_charge_stack())) + mineunit:execute_globalstep(1) + assert.equals(2, technic.get_RE_charge(get_charge_stack())) + end) + + it("t1_10001 can be charged", function() + -- Add tool to battery box + set_charge_stack(ItemStack("mymod:t1_10001")) + -- Test charging, 10kEU / cycle + assert.equals(0, technic.get_RE_charge(get_charge_stack())) + mineunit:execute_globalstep(1) + assert.equals(10000, technic.get_RE_charge(get_charge_stack())) + mineunit:execute_globalstep(1) + assert.equals(10001, technic.get_RE_charge(get_charge_stack())) + end) + + it("t1_65535 can be charged", function() + -- Add tool to battery box + set_charge_stack(ItemStack("mymod:t1_65535")) + -- Test charging, 10kEU / cycle + assert.equals(0, technic.get_RE_charge(get_charge_stack())) + for i=1,6 do mineunit:execute_globalstep(1) end + assert.equals(60000, technic.get_RE_charge(get_charge_stack())) + mineunit:execute_globalstep(1) + assert.equals(65535, technic.get_RE_charge(get_charge_stack())) + end) + + it("t1_65536 can be charged", function() + -- Add tool to battery box + set_charge_stack(ItemStack("mymod:t1_65536")) + -- Test charging, 10kEU / cycle + assert.equals(0, technic.get_RE_charge(get_charge_stack())) + for i=1,6 do mineunit:execute_globalstep(1) end + assert.equals(60000, technic.get_RE_charge(get_charge_stack())) + mineunit:execute_globalstep(1) + assert.equals(65536, technic.get_RE_charge(get_charge_stack())) + end) + + it("t100_6553500 can be charged", function() + -- Add tool to battery box + set_charge_stack(ItemStack("mymod:t100_6553500")) + -- Test charging, 10kEU / cycle + assert.equals(0, technic.get_RE_charge(get_charge_stack())) + for i=1,6 do mineunit:execute_globalstep(1) end + assert.equals(60000, technic.get_RE_charge(get_charge_stack())) + mineunit:execute_globalstep(1) + assert.equals(70000, technic.get_RE_charge(get_charge_stack())) + end) + + it("t100_6553600 can be charged", function() + -- Add tool to battery box + set_charge_stack(ItemStack("mymod:t100_6553600")) + -- Test charging, 10kEU / cycle + assert.equals(0, technic.get_RE_charge(get_charge_stack())) + for i=1,7 do mineunit:execute_globalstep(1) end + -- This tool already has small charge error and it is acceptable as long as error stays small + -- Charge value must be 69999-70001 after 7 charge cycles + assert.lt(69998, technic.get_RE_charge(get_charge_stack())) + assert.gt(70002, technic.get_RE_charge(get_charge_stack())) + end) + + it("t100_6553600 can be used", function() + -- Add tool to battery box + local stack = ItemStack("mymod:t100_6553600") + technic.set_RE_charge(stack, 700) + set_player_stack(stack) + -- Test using, 100 / cycle + for i=1,6 do player:do_use({x=0, y=0, z=0}) end + assert.equals(100, technic.get_RE_charge(get_player_stack())) + player:do_use({x=0, y=0, z=0}) + assert.equals(0, technic.get_RE_charge(get_player_stack())) + end) + + end) + + describe("Flashlight", function() + + local itemname = "technic:flashlight" + local itemdef = minetest.registered_items[itemname] + + setup(function() + set_charge_stack(ItemStack()) + set_discharge_stack(ItemStack()) + mineunit:execute_on_joinplayer(player) + end) + + teardown(function() + mineunit:execute_on_leaveplayer(player) + end) + + it("is registered", function() + assert.is_hashed(itemdef) + assert.is_function(itemdef.on_refill) + assert.equals("technic_RE_charge", itemdef.wear_represents) + assert.is_number(itemdef.technic_max_charge) + assert.gt(itemdef.technic_max_charge, 0) + end) + + it("charge is used", function() + -- Get fully charged item + local stack = ItemStack(itemname) + technic.set_RE_charge(stack, itemdef.technic_max_charge) + set_player_stack(stack) + + -- Use item, flashlight charge is used every globalstep and there's no on_use definition + spy.on(technic, "use_RE_charge") + for i=1, 100 do + mineunit:execute_globalstep(1) + end + assert.spy(technic.use_RE_charge).called(100) + + -- Check that item charge was actually used and error is acceptable + local charge_used = itemdef.technic_max_charge - technic.get_RE_charge(get_player_stack()) + local exact_use = 2 * 100 -- 2 per cycle / 100 cycles + assert.lt(0.9, charge_used / exact_use) + assert.gt(1.1, charge_used / exact_use) + end) + + end) + + describe("Multimeter", function() + + local itemname = "technic:multimeter" + local itemdef = minetest.registered_items[itemname] + + setup(function() + set_charge_stack(ItemStack()) + set_discharge_stack(ItemStack()) + mineunit:execute_on_joinplayer(player) + player:get_inventory():set_stack("main", 1, itemname) + end) + + teardown(function() + mineunit:execute_on_leaveplayer(player) + end) + + it("is registered", function() + assert.is_hashed(itemdef) + assert.is_function(itemdef.on_use) + assert.is_function(itemdef.on_refill) + assert.equals("technic_RE_charge", itemdef.wear_represents) + assert.is_number(itemdef.technic_max_charge) + assert.gt(itemdef.technic_max_charge, 0) + end) + + it("new item can be used", function() + spy.on(itemdef, "on_use") + player:do_use({x=0, y=0, z=0}) + assert.spy(itemdef.on_use).called(1) + end) + + it("has zero charge", function() + local stack = player:get_wielded_item() + assert.is_ItemStack(stack) + assert.is_false(stack:is_empty()) + assert.equals(0, technic.get_RE_charge(stack)) + end) + + it("can be charged", function() + -- Put item from player inventory to battery box src inventory + player:do_metadata_inventory_put(BB_Charge_POS, "src", 1) + + -- Verify that item charge is empty and charge in battery box for 30 seconds + assert.equals(0, technic.get_RE_charge(get_charge_stack())) + for i=0, 30 do + mineunit:execute_globalstep(1) + end + + -- Take item from battery box and check charge / wear values + player:do_metadata_inventory_take(BB_Charge_POS, "src", 1) + assert.gt(itemdef.technic_max_charge, 0) + assert.equals(itemdef.technic_max_charge, technic.get_RE_charge(get_player_stack())) + end) + + it("charge is used", function() + spy.on(itemdef, "on_use") + player:set_pos(vector.add(BB_Charge_POS, {x=0,y=1,z=0})) + player:do_use(BB_Charge_POS) + assert.spy(itemdef.on_use).called(1) + + -- Check that item charge was actually used and is not zero + local charge = technic.get_RE_charge(get_player_stack()) + assert.is_number(charge) + assert.gt(charge, 0) + assert.lt(charge, itemdef.technic_max_charge) + end) + + end) + + describe("technic_addons:chainsawmk3", function() + + -- Not running technic_addons:chainsawmk3 tests, this tools is just example of + -- actually available mod where max_charge vs use ratio goes over safe limits. + if not RUN_TECHNIC_ADDONS_CHAINSAWMK3_TESTS then return end + + local itemname = "technic_addons:chainsawmk3" + local itemdef = minetest.registered_items[itemname] + + setup(function() + world.add_layout({ + -- Some wood for chainsaw + {{{x=-10,y=0,z=-10},{x=10,y=10,z=10}}, "default:wood"}, + }) + set_charge_stack(ItemStack()) + set_discharge_stack(ItemStack()) + mineunit:execute_on_joinplayer(player) + set_player_stack(itemname) + end) + + teardown(function() + mineunit:execute_on_leaveplayer(player) + end) + + it("is registered", function() + assert.is_hashed(itemdef) + assert.is_function(itemdef.on_use) + assert.is_function(itemdef.on_refill) + assert.equals("technic_RE_charge", itemdef.wear_represents) + assert.is_number(itemdef.technic_max_charge) + assert.gt(itemdef.technic_max_charge, 0) + end) + + it("new item can be used", function() + spy.on(itemdef, "on_use") + player:do_use({x=0, y=0, z=0}) + assert.spy(itemdef.on_use).called(1) + end) + + it("has zero charge", function() + local stack = player:get_wielded_item() + assert.is_ItemStack(stack) + assert.is_false(stack:is_empty()) + assert.equals(0, technic.get_RE_charge(stack)) + end) + + it("can be charged", function() + -- Put item from player inventory to battery box src inventory + player:do_metadata_inventory_put(BB_Charge_POS, "src", 1) + + -- Verify that item charge is empty and charge in battery box for 30 seconds + assert.equals(0, technic.get_RE_charge(get_charge_stack())) + for i=0, math.ceil(itemdef.technic_max_charge / 10000) do + mineunit:execute_globalstep(1) + end + + -- Take item from battery box and check charge / wear values + player:do_metadata_inventory_take(BB_Charge_POS, "src", 1) + assert.gt(itemdef.technic_max_charge, 0) + assert.equals(itemdef.technic_max_charge, technic.get_RE_charge(get_player_stack())) + end) + + it("charge is used", function() + spy.on(itemdef, "on_use") + player:set_pos({x=0,y=1,z=0}) + player:do_use(player:get_pos()) + assert.spy(itemdef.on_use).called(1) + + -- Check that item charge was actually used and is not zero + local charge_used = itemdef.technic_max_charge - technic.get_RE_charge(get_player_stack()) + local accurate_use = 21 * 21 * 11 * 10 -- Area times use per node + local acceptable_error = accurate_use * 0.0001 + assert.gt(accurate_use + acceptable_error, charge_used) + assert.lt(accurate_use - acceptable_error, charge_used) + assert.equals(accurate_use, charge_used) + end) + + end) + +end) diff --git a/technic/tools/chainsaw.lua b/technic/tools/chainsaw.lua index 1a82d4d6..f964503a 100644 --- a/technic/tools/chainsaw.lua +++ b/technic/tools/chainsaw.lua @@ -1,8 +1,7 @@ -- Configuration local chainsaw_max_charge = 30000 -- Maximum charge of the saw --- Gives 2500 nodes on a single charge (about 50 complete normal trees) -local chainsaw_charge_per_node = 12 +local chainsaw_charge_per_node = 12 -- Gives 2500 nodes on a single charge (about 50 complete normal trees) local chainsaw_leaves = true -- Cut down tree leaves. -- Leaf decay may cause slowness on large trees if this is disabled. @@ -14,9 +13,7 @@ local timber_nodenames = {} -- Cuttable nodes local max_saw_radius = 12 -- max x/z distance away from starting position to allow cutting -- Prevents forest destruction, increase for extra wide trees - -- Support for nodes not in any supported node groups (tree, leaves, leafdecay, leafdecay_drop) - timber_nodenames["default:papyrus"] = true timber_nodenames["default:cactus"] = true timber_nodenames["default:bush_stem"] = true @@ -46,11 +43,8 @@ if minetest.get_modpath("trunks") then end end - local S = technic.getter -technic.register_power_tool("technic:chainsaw", chainsaw_max_charge) - -- Table for saving what was sawed down local produced = {} @@ -149,8 +143,7 @@ end local function chainsaw_dig(pos, current_charge) -- Start sawing things down local remaining_charge = recursive_dig(pos, pos, current_charge) - minetest.sound_play("chainsaw", {pos = pos, gain = 1.0, - max_hear_distance = 10}) + minetest.sound_play("chainsaw", {pos = pos, gain = 1.0, max_hear_distance = 10}, true) -- Now drop items for the player for name, stack in pairs(produced) do @@ -171,21 +164,17 @@ local function chainsaw_dig(pos, current_charge) return remaining_charge end - -minetest.register_tool("technic:chainsaw", { +technic.register_power_tool("technic:chainsaw", { description = S("Chainsaw"), inventory_image = "technic_chainsaw.png", - stack_max = 1, - wear_represents = "technic_RE_charge", - on_refill = technic.refill_RE_charge, + max_charge = chainsaw_max_charge, on_use = function(itemstack, user, pointed_thing) if pointed_thing.type ~= "node" then - return itemstack + return end - local meta = minetest.deserialize(itemstack:get_metadata()) - if not meta or not meta.charge or - meta.charge < chainsaw_charge_per_node then + local charge = technic.get_RE_charge(itemstack) + if charge < chainsaw_charge_per_node then return end @@ -197,10 +186,9 @@ minetest.register_tool("technic:chainsaw", { -- Send current charge to digging function so that the -- chainsaw will stop after digging a number of nodes - meta.charge = chainsaw_dig(pointed_thing.under, meta.charge) + charge = chainsaw_dig(pointed_thing.under, charge) if not technic.creative_mode then - technic.set_RE_wear(itemstack, meta.charge, chainsaw_max_charge) - itemstack:set_metadata(minetest.serialize(meta)) + technic.set_RE_charge(itemstack, charge) end return itemstack end, diff --git a/technic/tools/flashlight.lua b/technic/tools/flashlight.lua index 84decbb4..faabc37f 100644 --- a/technic/tools/flashlight.lua +++ b/technic/tools/flashlight.lua @@ -5,16 +5,12 @@ local flashlight_max_charge = 30000 local S = technic.getter -technic.register_power_tool("technic:flashlight", flashlight_max_charge) - minetest.register_alias("technic:light_off", "air") -minetest.register_tool("technic:flashlight", { +technic.register_power_tool("technic:flashlight", { description = S("Flashlight"), inventory_image = "technic_flashlight.png", - stack_max = 1, - wear_represents = "technic_RE_charge", - on_refill = technic.refill_RE_charge, + max_charge = flashlight_max_charge, }) minetest.register_craft({ @@ -26,7 +22,6 @@ minetest.register_craft({ } }) - local player_positions = {} local was_wielding = {} @@ -37,17 +32,10 @@ local function check_for_flashlight(player) local inv = player:get_inventory() local hotbar = inv:get_list("main") for i = 1, 8 do - if hotbar[i]:get_name() == "technic:flashlight" then - local meta = minetest.deserialize(hotbar[i]:get_metadata()) - if meta and meta.charge and meta.charge >= 2 then - if not technic.creative_mode then - meta.charge = meta.charge - 2; - technic.set_RE_wear(hotbar[i], meta.charge, flashlight_max_charge) - hotbar[i]:set_metadata(minetest.serialize(meta)) - inv:set_stack("main", i, hotbar[i]) - end - return true - end + if hotbar[i]:get_name() == "technic:flashlight" and technic.use_RE_charge(hotbar[i], 2) then + -- See https://github.com/minetest/minetest/issues/9377 for wield item animation + inv:set_stack("main", i, hotbar[i]) + return true end end return false @@ -62,7 +50,6 @@ minetest.register_on_joinplayer(function(player) was_wielding[player_name] = true end) - minetest.register_on_leaveplayer(function(player) local player_name = player:get_player_name() local pos = player_positions[player_name] diff --git a/technic/tools/mining_drill.lua b/technic/tools/mining_drill.lua index 692b74bb..0dc383ff 100644 --- a/technic/tools/mining_drill.lua +++ b/technic/tools/mining_drill.lua @@ -7,7 +7,7 @@ minetest.register_craft({ output = 'technic:mining_drill', recipe = { {'default:tin_ingot', 'technic:diamond_drill_head', 'default:tin_ingot'}, - {'technic:stainless_steel_ingot', 'basic_materials:motor', 'technic:stainless_steel_ingot'}, + {'technic:stainless_steel_ingot', 'basic_materials:motor', 'technic:stainless_steel_ingot'}, {'', 'technic:red_energy_crystal', 'default:copper_ingot'}, } }) @@ -46,6 +46,11 @@ local mining_drill_mode_text = { {S("3x3 nodes.")}, } +local function get_description(mk, mode) + local description = "Mining Drill Mk@1"..(mode > 0 and " Mode @2" or "") + return mode > 0 and S(description, mk, mode) or S(description, mk) +end + local function drill_dig_it0 (pos,player) if minetest.is_protected(pos, player:get_player_name()) then minetest.record_protection_violation(pos, player:get_player_name()) @@ -230,7 +235,7 @@ local function drill_dig_it(pos, player, mode) end end - minetest.sound_play("mining_drill", {pos = pos, gain = 1.0, max_hear_distance = 10,}) + minetest.sound_play("mining_drill", {pos = pos, gain = 1.0, max_hear_distance = 10}, true) end local function pos_is_pointable(pos) @@ -240,172 +245,113 @@ local function pos_is_pointable(pos) end local function mining_drill_mk2_setmode(user,itemstack) - local player_name=user:get_player_name() - local item=itemstack:to_table() - local meta=minetest.deserialize(item["metadata"]) or {} - if meta["mode"]==nil then + local player_name = user:get_player_name() + local meta = itemstack:get_meta() + local mode = meta:get_int("mode") + if mode == 0 then minetest.chat_send_player(player_name, S("Use while sneaking to change Mining Drill Mk@1 modes.", 2)) - meta["mode"]=0 end - local mode=meta["mode"] - mode=mode+1 - if mode>=5 then mode=1 end - minetest.chat_send_player(player_name, - S("Mining Drill Mk@1 Mode @2", 2, mode)..": "..mining_drill_mode_text[mode][1]) - itemstack:set_name("technic:mining_drill_mk2_"..mode); - meta["mode"]=mode - itemstack:set_metadata(minetest.serialize(meta)) + mode = mode < 4 and mode + 1 or 1 + minetest.chat_send_player(player_name, get_description(2, mode)..": "..mining_drill_mode_text[mode][1]) + itemstack:set_name("technic:mining_drill_mk2_"..mode) + meta:set_int("mode", mode) return itemstack end local function mining_drill_mk3_setmode(user,itemstack) - local player_name=user:get_player_name() - local item=itemstack:to_table() - local meta=minetest.deserialize(item["metadata"]) or {} - if meta["mode"]==nil then + local player_name = user:get_player_name() + local meta = itemstack:get_meta() + local mode = meta:get_int("mode") + if mode == 0 then minetest.chat_send_player(player_name, S("Use while sneaking to change Mining Drill Mk@1 modes.", 3)) - meta["mode"]=0 end - local mode=meta["mode"] - mode=mode+1 - if mode>=6 then mode=1 end - minetest.chat_send_player(player_name, - S("Mining Drill Mk@1 Mode @2", 3, mode)..": "..mining_drill_mode_text[mode][1]) - itemstack:set_name("technic:mining_drill_mk3_"..mode); - meta["mode"]=mode - itemstack:set_metadata(minetest.serialize(meta)) + mode = mode < 5 and mode + 1 or 1 + minetest.chat_send_player(player_name, get_description(3, mode)..": "..mining_drill_mode_text[mode][1]) + itemstack:set_name("technic:mining_drill_mk3_"..mode) + meta:set_int("mode", mode) return itemstack end - local function mining_drill_mk2_handler(itemstack, user, pointed_thing) local keys = user:get_player_control() - local meta = minetest.deserialize(itemstack:get_metadata()) - if not meta or not meta.mode or keys.sneak then + local meta = itemstack:get_meta() + local mode = meta:get_int("mode") + if mode == 0 or keys.sneak then return mining_drill_mk2_setmode(user, itemstack) end - if pointed_thing.type ~= "node" or not pos_is_pointable(pointed_thing.under) or not meta.charge then + if pointed_thing.type ~= "node" or not pos_is_pointable(pointed_thing.under) then return end - local charge_to_take = cost_to_use(2, meta.mode) - if meta.charge >= charge_to_take then + local charge_to_take = cost_to_use(2, mode) + if technic.use_RE_charge(itemstack, charge_to_take) then local pos = minetest.get_pointed_thing_position(pointed_thing, false) - drill_dig_it(pos, user, meta.mode) - if not technic.creative_mode then - meta.charge = meta.charge - charge_to_take - itemstack:set_metadata(minetest.serialize(meta)) - technic.set_RE_wear(itemstack, meta.charge, max_charge[2]) - end + drill_dig_it(pos, user, mode) end return itemstack end local function mining_drill_mk3_handler(itemstack, user, pointed_thing) local keys = user:get_player_control() - local meta = minetest.deserialize(itemstack:get_metadata()) - if not meta or not meta.mode or keys.sneak then + local meta = itemstack:get_meta() + local mode = meta:get_int("mode") + if mode == 0 or keys.sneak then return mining_drill_mk3_setmode(user, itemstack) end - if pointed_thing.type ~= "node" or not pos_is_pointable(pointed_thing.under) or not meta.charge then + if pointed_thing.type ~= "node" or not pos_is_pointable(pointed_thing.under) then return end - local charge_to_take = cost_to_use(3, meta.mode) - if meta.charge >= charge_to_take then + local charge_to_take = cost_to_use(3, mode) + if technic.use_RE_charge(itemstack, charge_to_take) then local pos = minetest.get_pointed_thing_position(pointed_thing, false) - drill_dig_it(pos, user, meta.mode) - if not technic.creative_mode then - meta.charge = meta.charge - charge_to_take - itemstack:set_metadata(minetest.serialize(meta)) - technic.set_RE_wear(itemstack, meta.charge, max_charge[3]) - end + drill_dig_it(pos, user, mode) end return itemstack end -technic.register_power_tool("technic:mining_drill", max_charge[1]) - -minetest.register_tool("technic:mining_drill", { - description = S("Mining Drill Mk@1", 1), +-- register Mining Drill Mk1 +technic.register_power_tool("technic:mining_drill", { + description = get_description(1, 0), inventory_image = "technic_mining_drill.png", - stack_max = 1, - wear_represents = "technic_RE_charge", - on_refill = technic.refill_RE_charge, + max_charge = max_charge[1], on_use = function(itemstack, user, pointed_thing) if pointed_thing.type ~= "node" or not pos_is_pointable(pointed_thing.under) then return itemstack end - local meta = minetest.deserialize(itemstack:get_metadata()) - if not meta or not meta.charge then - return - end local charge_to_take = cost_to_use(1, 1) - if meta.charge >= charge_to_take then + if technic.use_RE_charge(itemstack, charge_to_take) then local pos = minetest.get_pointed_thing_position(pointed_thing, false) drill_dig_it(pos, user, 1) - if not technic.creative_mode then - meta.charge = meta.charge - charge_to_take - itemstack:set_metadata(minetest.serialize(meta)) - technic.set_RE_wear(itemstack, meta.charge, max_charge[1]) - end end return itemstack end, }) -minetest.register_tool("technic:mining_drill_mk2", { - description = S("Mining Drill Mk@1", 2), - inventory_image = "technic_mining_drill_mk2.png", - wear_represents = "technic_RE_charge", - on_refill = technic.refill_RE_charge, - on_use = function(itemstack, user, pointed_thing) - mining_drill_mk2_handler(itemstack, user, pointed_thing) - return itemstack - end, -}) - -technic.register_power_tool("technic:mining_drill_mk2", max_charge[2]) - -for i = 1, 4 do - technic.register_power_tool("technic:mining_drill_mk2_"..i, max_charge[2]) - minetest.register_tool("technic:mining_drill_mk2_"..i, { - description = S("Mining Drill Mk@1 Mode @2", 2, i), - inventory_image = "technic_mining_drill_mk2.png^technic_tool_mode"..i..".png", - wield_image = "technic_mining_drill_mk2.png", - wear_represents = "technic_RE_charge", - on_refill = technic.refill_RE_charge, - groups = {not_in_creative_inventory=1}, - on_use = function(itemstack, user, pointed_thing) - mining_drill_mk2_handler(itemstack, user, pointed_thing) - return itemstack - end, - }) +do -- register Mining Drill Mk2 + local inventory_image = "technic_mining_drill_mk2.png" + for i = 0, 4 do + local overlay = i > 0 and "^technic_tool_mode"..i..".png" or "" + technic.register_power_tool("technic:mining_drill_mk2"..(i > 0 and ("_"..i) or ""), { + description = get_description(2, i), + inventory_image = inventory_image .. overlay, + wield_image = inventory_image, + max_charge = max_charge[2], + groups = i > 0 and {not_in_creative_inventory=1} or nil, + on_use = mining_drill_mk2_handler, + }) + end end -minetest.register_tool("technic:mining_drill_mk3", { - description = S("Mining Drill Mk@1", 3), - inventory_image = "technic_mining_drill_mk3.png", - wear_represents = "technic_RE_charge", - on_refill = technic.refill_RE_charge, - on_use = function(itemstack, user, pointed_thing) - mining_drill_mk3_handler(itemstack,user,pointed_thing) - return itemstack - end, -}) - -technic.register_power_tool("technic:mining_drill_mk3", max_charge[3]) - -for i=1,5,1 do - technic.register_power_tool("technic:mining_drill_mk3_"..i, max_charge[3]) - minetest.register_tool("technic:mining_drill_mk3_"..i, { - description = S("Mining Drill Mk@1 Mode @2", 3, i), - inventory_image = "technic_mining_drill_mk3.png^technic_tool_mode"..i..".png", - wield_image = "technic_mining_drill_mk3.png", - wear_represents = "technic_RE_charge", - on_refill = technic.refill_RE_charge, - groups = {not_in_creative_inventory=1}, - on_use = function(itemstack, user, pointed_thing) - mining_drill_mk3_handler(itemstack,user,pointed_thing) - return itemstack - end, - }) +do -- register Mining Drill Mk3 + local inventory_image = "technic_mining_drill_mk3.png" + for i = 0, 5 do + local overlay = i > 0 and "^technic_tool_mode"..i..".png" or "" + technic.register_power_tool("technic:mining_drill_mk3"..(i > 0 and ("_"..i) or ""), { + description = get_description(3, i), + inventory_image = inventory_image .. overlay, + wield_image = inventory_image, + max_charge = max_charge[3], + groups = i > 0 and {not_in_creative_inventory=1} or nil, + on_use = mining_drill_mk3_handler, + }) + end end diff --git a/technic/tools/mining_lasers.lua b/technic/tools/mining_lasers.lua index 1fc6570e..bc8f2a6b 100644 --- a/technic/tools/mining_lasers.lua +++ b/technic/tools/mining_lasers.lua @@ -74,7 +74,7 @@ local function laser_shoot(player, range, particle_texture, sound) size = 1, texture = particle_texture .. "^[transform" .. math.random(0, 7), }) - minetest.sound_play(sound, {pos = player_pos, max_hear_distance = range}) + minetest.sound_play(sound, {pos = player_pos, max_hear_distance = range}, true) for pos in technic.trace_node_ray_fat(start_pos, dir, range) do if minetest.is_protected(pos, player_name) then minetest.record_protection_violation(pos, player_name) @@ -92,36 +92,26 @@ local function laser_shoot(player, range, particle_texture, sound) end for _, m in pairs(mining_lasers_list) do - technic.register_power_tool("technic:laser_mk"..m[1], m[3]) - minetest.register_tool("technic:laser_mk"..m[1], { + technic.register_power_tool("technic:laser_mk"..m[1], { description = S("Mining Laser Mk@1", m[1]), inventory_image = "technic_mining_laser_mk"..m[1]..".png", range = 0, - stack_max = 1, - wear_represents = "technic_RE_charge", - on_refill = technic.refill_RE_charge, + max_charge = m[3], on_use = function(itemstack, user) - local meta = minetest.deserialize(itemstack:get_metadata()) - if not meta or not meta.charge or meta.charge == 0 then - return - end - - local range = m[2] - if meta.charge < m[4] then - if not allow_entire_discharging then - return + local charge = technic.get_RE_charge(itemstack) + if charge > 0 then + local range = m[2] + if charge < m[4] then + if not allow_entire_discharging then + return + end + -- If charge is too low, give the laser a shorter range + range = range * charge / m[4] end - -- If charge is too low, give the laser a shorter range - range = range * meta.charge / m[4] - end - laser_shoot(user, range, "technic_laser_beam_mk" .. m[1] .. ".png", - "technic_laser_mk" .. m[1]) - if not technic.creative_mode then - meta.charge = math.max(meta.charge - m[4], 0) - technic.set_RE_wear(itemstack, meta.charge, m[3]) - itemstack:set_metadata(minetest.serialize(meta)) + technic.use_RE_charge(itemstack, math.min(m[4], charge)) + laser_shoot(user, range, "technic_laser_beam_mk" .. m[1] .. ".png", "technic_laser_mk" .. m[1]) + return itemstack end - return itemstack end, }) end diff --git a/technic/tools/multimeter.lua b/technic/tools/multimeter.lua index 12bcb747..e32fc565 100644 --- a/technic/tools/multimeter.lua +++ b/technic/tools/multimeter.lua @@ -63,21 +63,7 @@ minetest.register_craft({ }) local function use_charge(itemstack, multiplier) - if technic.creative_mode then - -- Do not check charge in creative mode - return true - end - local real_pwr_use = power_usage * (multiplier or 1) - local meta = minetest.deserialize(itemstack:get_metadata()) - if not meta or not meta.charge or meta.charge < real_pwr_use then - -- Not enough energy available - return false - end - meta.charge = meta.charge - real_pwr_use - technic.set_RE_wear(itemstack, meta.charge, max_charge) - itemstack:set_metadata(minetest.serialize(meta)) - -- Charge used successfully - return true + return technic.use_RE_charge(itemstack, power_usage * (multiplier or 1)) end local function async_itemstack_get(player, refstack) @@ -298,16 +284,12 @@ local function check_node(pos) end end -technic.register_power_tool("technic:multimeter", max_charge) - -minetest.register_tool("technic:multimeter", { +technic.register_power_tool("technic:multimeter", { description = S("Multimeter"), inventory_image = texture, wield_image = texture, - stack_max = 1, liquids_pointable = false, - wear_represents = "technic_RE_charge", - on_refill = technic.refill_RE_charge, + max_charge = max_charge, on_use = function(itemstack, player, pointed_thing) local pos = minetest.get_pointed_thing_position(pointed_thing, false) if pos and pointed_thing.type == "node" then diff --git a/technic/tools/prospector.lua b/technic/tools/prospector.lua index 948294e4..7b03d98c 100644 --- a/technic/tools/prospector.lua +++ b/technic/tools/prospector.lua @@ -1,82 +1,90 @@ local S = technic.getter -technic.register_power_tool("technic:prospector", 300000) +local function migrate_meta(meta) + local data = meta:get("") + if data then + local fields = minetest.deserialize(data, true) + if type(fields) == "table" then + meta:set_string("target", fields.target) + meta:set_int("look_radius", fields.look_radius or 1) + meta:set_int("look_depth", fields.look_depth or 7) + meta:set_string("", "") + return meta:get("target") + end + end +end -local function get_metadata(toolstack) - local m = minetest.deserialize(toolstack:get_metadata()) - if not m then m = {} end - if not m.charge then m.charge = 0 end - if not m.target then m.target = "" end - if not m.look_depth then m.look_depth = 7 end - if not m.look_radius then m.look_radius = 1 end - return m +local function get_field(meta) + return (meta:get("look_depth") or 7), (meta:get("look_radius") or 1) end -minetest.register_tool("technic:prospector", { +technic.register_power_tool("technic:prospector", { description = S("Prospector"), inventory_image = "technic_prospector.png", - wear_represents = "technic_RE_charge", - on_refill = technic.refill_RE_charge, + max_charge = 300000, on_use = function(toolstack, user, pointed_thing) if not user or not user:is_player() or user.is_fake_player then return end if pointed_thing.type ~= "node" then return end - local toolmeta = get_metadata(toolstack) - local look_diameter = toolmeta.look_radius * 2 + 1 - local charge_to_take = toolmeta.look_depth * (toolmeta.look_depth + 1) * look_diameter * look_diameter - if toolmeta.charge < charge_to_take then return end - if toolmeta.target == "" then + local meta = toolstack:get_meta() + local target = meta:get("target") or migrate_meta(meta) + if not target then minetest.chat_send_player(user:get_player_name(), "Right-click to set target block type") - return + return toolstack end - if not technic.creative_mode then - toolmeta.charge = toolmeta.charge - charge_to_take - toolstack:set_metadata(minetest.serialize(toolmeta)) - technic.set_RE_wear(toolstack, toolmeta.charge, technic.power_tools[toolstack:get_name()]) + local look_depth, look_radius = get_field(meta) + local look_diameter = look_radius * 2 + 1 + local charge_to_take = look_depth * (look_depth + 1) * look_diameter * look_diameter + if not technic.use_RE_charge(toolstack, charge_to_take) then + return toolstack end local start_pos = pointed_thing.under local forward = minetest.facedir_to_dir(minetest.dir_to_facedir(user:get_look_dir(), true)) local right = forward.x ~= 0 and { x=0, y=1, z=0 } or (forward.y ~= 0 and { x=0, y=0, z=1 } or { x=1, y=0, z=0 }) local up = forward.x ~= 0 and { x=0, y=0, z=1 } or (forward.y ~= 0 and { x=1, y=0, z=0 } or { x=0, y=1, z=0 }) - local base_pos = vector.add(start_pos, vector.multiply(vector.add(right, up), - toolmeta.look_radius)) + local base_pos = vector.add(start_pos, vector.multiply(vector.add(right, up), - look_radius)) local found = false - for f = 0, toolmeta.look_depth-1 do + for f = 0, look_depth-1 do for r = 0, look_diameter-1 do for u = 0, look_diameter-1 do if minetest.get_node(vector.add(vector.add(vector.add(base_pos, vector.multiply(forward, f)), - vector.multiply(right, r)), vector.multiply(up, u))).name == toolmeta.target then + vector.multiply(right, r)), vector.multiply(up, u))).name == target then found = true end end end end if math.random() < 0.02 then found = not found end - minetest.chat_send_player(user:get_player_name(), minetest.registered_nodes[toolmeta.target].description.. + minetest.chat_send_player(user:get_player_name(), minetest.registered_nodes[target].description.. " is "..(found and "present" or "absent").." in "..look_diameter.. - "x"..look_diameter.."x"..toolmeta.look_depth.." region") + "x"..look_diameter.."x"..look_depth.." region") minetest.sound_play("technic_prospector_"..(found and "hit" or "miss"), - { pos = vector.add(user:get_pos(), { x = 0, y = 1, z = 0 }), gain = 1.0, max_hear_distance = 10 }) + { pos = vector.add(user:get_pos(), { x = 0, y = 1, z = 0 }), gain = 1.0, max_hear_distance = 10 }, true) return toolstack end, on_place = function(toolstack, user, pointed_thing) - if not user or not user:is_player() or user.is_fake_player then return end - local toolmeta = get_metadata(toolstack) + if not user or not user:is_player() or user.is_fake_player then + return + end + local meta = toolstack:get_meta() + local target = meta:get("target") or migrate_meta(meta) + local look_depth, look_radius = get_field(meta) local pointed if pointed_thing.type == "node" then local pname = minetest.get_node(pointed_thing.under).name local pdef = minetest.registered_nodes[pname] - if pdef and (pdef.groups.not_in_creative_inventory or 0) == 0 and pname ~= toolmeta.target then + if pdef and (pdef.groups.not_in_creative_inventory or 0) == 0 and pname ~= target then pointed = pname end end - local look_diameter = toolmeta.look_radius * 2 + 1 + local look_diameter = look_radius * 2 + 1 minetest.show_formspec(user:get_player_name(), "technic:prospector_control", "size[7,8.5]".. "item_image[0,0;1,1;"..toolstack:get_name().."]".. "label[1,0;"..minetest.formspec_escape(toolstack:get_definition().description).."]".. - (toolmeta.target ~= "" and + (target and "label[0,1.5;Current target:]".. - "label[0,2;"..minetest.formspec_escape(minetest.registered_nodes[toolmeta.target].description).."]".. - "item_image[0,2.5;1,1;"..toolmeta.target.."]" or + "label[0,2;"..minetest.formspec_escape(minetest.registered_nodes[target].description).."]".. + "item_image[0,2.5;1,1;"..target.."]" or "label[0,1.5;No target set]").. (pointed and "label[3.5,1.5;May set new target:]".. @@ -91,35 +99,38 @@ minetest.register_tool("technic:prospector", { "button_exit[4.5,5.15;1,0.5;look_radius_1;3x3]".. "button_exit[5.5,5.15;1,0.5;look_radius_3;7x7]".. "label[0,6;Region depth:]".. - "label[0,6.5;"..toolmeta.look_depth.."]".. + "label[0,6.5;"..look_depth.."]".. "label[3.5,6;Set region depth:]".. "button_exit[3.5,6.65;1,0.5;look_depth_7;7]".. "button_exit[4.5,6.65;1,0.5;look_depth_14;14]".. "button_exit[5.5,6.65;1,0.5;look_depth_21;21]".. "label[0,7.5;Accuracy:]".. "label[0,8;98%]") - return + return toolstack end, }) minetest.register_on_player_receive_fields(function(user, formname, fields) - if formname ~= "technic:prospector_control" then return false end - if not user or not user:is_player() or user.is_fake_player then return end + if formname ~= "technic:prospector_control" then + return false + end + if not user or not user:is_player() or user.is_fake_player then + return + end local toolstack = user:get_wielded_item() - if toolstack:get_name() ~= "technic:prospector" then return true end - local toolmeta = get_metadata(toolstack) + if toolstack:get_name() ~= "technic:prospector" then + return true + end + local meta = toolstack:get_meta() for field, value in pairs(fields) do if field:sub(1, 7) == "target_" then - toolmeta.target = field:sub(8) - end - if field:sub(1, 12) == "look_radius_" then - toolmeta.look_radius = field:sub(13) - end - if field:sub(1, 11) == "look_depth_" then - toolmeta.look_depth = field:sub(12) + meta:set_string("target", field:sub(8)) + elseif field:sub(1, 12) == "look_radius_" then + meta:set_int("look_radius", field:sub(13)) + elseif field:sub(1, 11) == "look_depth_" then + meta:set_int("look_depth", field:sub(12)) end end - toolstack:set_metadata(minetest.serialize(toolmeta)) user:set_wielded_item(toolstack) return true end) diff --git a/technic/tools/sonic_screwdriver.lua b/technic/tools/sonic_screwdriver.lua index 536f47c3..a94878ee 100644 --- a/technic/tools/sonic_screwdriver.lua +++ b/technic/tools/sonic_screwdriver.lua @@ -2,8 +2,6 @@ local sonic_screwdriver_max_charge = 15000 local S = technic.getter -technic.register_power_tool("technic:sonic_screwdriver", sonic_screwdriver_max_charge) - -- screwdriver handler code reused from minetest/minetest_game screwdriver @a9ac480 local ROTATE_FACE = 1 local ROTATE_AXIS = 2 @@ -41,12 +39,11 @@ local function screwdriver_handler(itemstack, user, pointed_thing, mode) -- contrary to the default screwdriver, do not check for can_dig, to allow rotating machines with CLU's in them -- this is consistent with the previous sonic screwdriver - local meta1 = minetest.deserialize(itemstack:get_metadata()) - if not meta1 or not meta1.charge or meta1.charge < 100 then + if not technic.use_RE_charge(itemstack, 100) then return end - minetest.sound_play("technic_sonic_screwdriver", {pos = pos, gain = 0.3, max_hear_distance = 10}) + minetest.sound_play("technic_sonic_screwdriver", {pos = pos, gain = 0.3, max_hear_distance = 10}, true) -- Set param2 local rotationPart = node.param2 % 32 -- get first 4 bits @@ -63,27 +60,18 @@ local function screwdriver_handler(itemstack, user, pointed_thing, mode) node.param2 = preservePart + rotationPart minetest.swap_node(pos, node) - if not technic.creative_mode then - meta1.charge = meta1.charge - 100 - itemstack:set_metadata(minetest.serialize(meta1)) - technic.set_RE_wear(itemstack, meta1.charge, sonic_screwdriver_max_charge) - end - return itemstack end -minetest.register_tool("technic:sonic_screwdriver", { +technic.register_power_tool("technic:sonic_screwdriver", { description = S("Sonic Screwdriver (left-click rotates face, right-click rotates axis)"), inventory_image = "technic_sonic_screwdriver.png", - wear_represents = "technic_RE_charge", - on_refill = technic.refill_RE_charge, + max_charge = sonic_screwdriver_max_charge, on_use = function(itemstack, user, pointed_thing) - screwdriver_handler(itemstack, user, pointed_thing, ROTATE_FACE) - return itemstack + return screwdriver_handler(itemstack, user, pointed_thing, ROTATE_FACE) end, on_place = function(itemstack, user, pointed_thing) - screwdriver_handler(itemstack, user, pointed_thing, ROTATE_AXIS) - return itemstack + return screwdriver_handler(itemstack, user, pointed_thing, ROTATE_AXIS) end, }) @@ -95,4 +83,3 @@ minetest.register_craft({ {"mesecons_materials:fiber", "moreores:mithril_ingot", "mesecons_materials:fiber"} } }) - diff --git a/technic/tools/vacuum.lua b/technic/tools/vacuum.lua index 7b01af88..77808f61 100644 --- a/technic/tools/vacuum.lua +++ b/technic/tools/vacuum.lua @@ -5,49 +5,38 @@ local vacuum_range = 8 -- 8 - Area in which to pick up objec local S = technic.getter -technic.register_power_tool("technic:vacuum", vacuum_max_charge) - -minetest.register_tool("technic:vacuum", { +technic.register_power_tool("technic:vacuum", { description = S("Vacuum Cleaner"), inventory_image = "technic_vacuum.png", - stack_max = 1, - wear_represents = "technic_RE_charge", - on_refill = technic.refill_RE_charge, + max_charge = vacuum_max_charge, on_use = function(itemstack, user, pointed_thing) - local meta = minetest.deserialize(itemstack:get_metadata()) - if not meta or not meta.charge then + local original_charge = technic.get_RE_charge(itemstack) + if original_charge < vacuum_charge_per_object then return end - if meta.charge > vacuum_charge_per_object then - minetest.sound_play("vacuumcleaner", { - to_player = user:get_player_name(), - gain = 0.4, - }) - end + minetest.sound_play("vacuumcleaner", {to_player = user:get_player_name(), gain = 0.4}, true) local pos = user:get_pos() local inv = user:get_inventory() + local charge = original_charge for _, object in ipairs(minetest.get_objects_inside_radius(pos, vacuum_range)) do - local luaentity = object:get_luaentity() - if not object:is_player() and luaentity and luaentity.name == "__builtin:item" and luaentity.itemstring ~= "" then - if inv and inv:room_for_item("main", ItemStack(luaentity.itemstring)) then - meta.charge = meta.charge - vacuum_charge_per_object - if meta.charge < vacuum_charge_per_object then - return - end - inv:add_item("main", ItemStack(luaentity.itemstring)) - minetest.sound_play("item_drop_pickup", { - to_player = user:get_player_name(), - gain = 0.4, - }) - luaentity.itemstring = "" + local entity = object:get_luaentity() + if not object:is_player() and entity and entity.name == "__builtin:item" and entity.itemstring ~= "" then + if inv and inv:room_for_item("main", ItemStack(entity.itemstring)) then + charge = charge - vacuum_charge_per_object + inv:add_item("main", ItemStack(entity.itemstring)) + minetest.sound_play("item_drop_pickup", {to_player = user:get_player_name(), gain = 0.4}, true) + entity.itemstring = "" object:remove() + if charge < vacuum_charge_per_object then + break + end end end end - - technic.set_RE_wear(itemstack, meta.charge, vacuum_max_charge) - itemstack:set_metadata(minetest.serialize(meta)) - return itemstack + if not technic.creative_mode and charge ~= original_charge then + technic.set_RE_charge(itemstack, charge) + return itemstack + end end, })