From 223c90e68424109fb8f3e49a847327135e675907 Mon Sep 17 00:00:00 2001 From: OgelGames Date: Mon, 27 May 2024 00:31:04 +1000 Subject: [PATCH] Fake player and wielder improvements (#126) --- .luacheckrc | 2 +- autoplace_tubes.lua | 6 +- common.lua | 318 +---------------------- filter-injector.lua | 10 +- legacy.lua | 62 ----- mod.conf | 2 +- wielder.lua | 614 ++++++++++++++++++-------------------------- 7 files changed, 262 insertions(+), 752 deletions(-) delete mode 100644 legacy.lua diff --git a/.luacheckrc b/.luacheckrc index 215a236..6d2f92f 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -21,6 +21,6 @@ read_globals = { "default", "mesecon", "digiline", "screwdriver", "unified_inventory", "i3", "mcl_experience", "awards", - "xcompat", + "xcompat", "fakelib", } diff --git a/autoplace_tubes.lua b/autoplace_tubes.lua index 8348430..9f4e112 100644 --- a/autoplace_tubes.lua +++ b/autoplace_tubes.lua @@ -11,7 +11,7 @@ local function nodeside(node, tubedir) end local backdir = minetest.facedir_to_dir(node.param2) - local back = pipeworks.vector_dot(backdir, tubedir) + local back = vector.dot(backdir, tubedir) if back == 1 then return "back" elseif back == -1 then @@ -19,7 +19,7 @@ local function nodeside(node, tubedir) end local topdir = pipeworks.facedir_to_top_dir(node.param2) - local top = pipeworks.vector_dot(topdir, tubedir) + local top = vector.dot(topdir, tubedir) if top == 1 then return "top" elseif top == -1 then @@ -27,7 +27,7 @@ local function nodeside(node, tubedir) end local rightdir = pipeworks.facedir_to_right_dir(node.param2) - local right = pipeworks.vector_dot(rightdir, tubedir) + local right = vector.dot(rightdir, tubedir) if right == 1 then return "right" else diff --git a/common.lua b/common.lua index 5db3feb..dcf2c7f 100644 --- a/common.lua +++ b/common.lua @@ -91,22 +91,6 @@ function pipeworks.replace_name(tbl,tr,name) return ntbl end ----------------------- --- Vector functions -- ----------------------- - -function pipeworks.vector_cross(a, b) - return { - x = a.y * b.z - a.z * b.y, - y = a.z * b.x - a.x * b.z, - z = a.x * b.y - a.y * b.x - } -end - -function pipeworks.vector_dot(a, b) - return a.x * b.x + a.y * b.y + a.z * b.z -end - ----------------------- -- Facedir functions -- ----------------------- @@ -122,7 +106,7 @@ function pipeworks.facedir_to_top_dir(facedir) end function pipeworks.facedir_to_right_dir(facedir) - return pipeworks.vector_cross( + return vector.cross( pipeworks.facedir_to_top_dir(facedir), minetest.facedir_to_dir(facedir) ) @@ -142,29 +126,10 @@ function directions.side_to_dir(side) end function directions.dir_to_side(dir) - local c = pipeworks.vector_dot(dir, vector.new(1, 2, 3)) + 4 + local c = vector.dot(dir, vector.new(1, 2, 3)) + 4 return ({6, 2, 4, 0, 3, 1, 5})[c] end ----------------------- --- String functions -- ----------------------- - ---[[function pipeworks.string_split(str, sep) - local fields = {} - local index = 1 - local expr = "([^"..sep.."])+" - string.gsub(str, expr, function(substring) - fields[index] = substring - index = index + 1 - end) - return fields -end]] - -function pipeworks.string_startswith(str, substr) - return str:sub(1, substr:len()) == substr -end - --------------------- -- Table functions -- --------------------- @@ -208,7 +173,7 @@ pipeworks.fs_helpers = fs_helpers function fs_helpers.on_receive_fields(pos, fields) local meta = minetest.get_meta(pos) for field in pairs(fields) do - if pipeworks.string_startswith(field, "fs_helpers_cycling:") then + if field:match("^fs_helpers_cycling:") then local l = field:split(":") local new_value = tonumber(l[2]) local meta_name = l[3] @@ -325,278 +290,5 @@ function pipeworks.load_position(pos) vm:read_from_map(pos, pos) end -local function delay(...) - local args = {...} - return (function() return unpack(args) end) -end - -local function get_set_wrap(name, is_dynamic) - return (function(self) - return self["_" .. name] - end), (function(self, value) - if is_dynamic then - self["_" .. name] = type(value) == "table" - and table.copy(value) or value - end - end) -end - -local fake_player_metatable = { - is_player = delay(true), - is_fake_player = true, - - -- dummy implementation of the rest of the player API: - add_player_velocity = delay(), -- deprecated - add_velocity = delay(), - get_acceleration = delay(), -- no-op for players - get_animation = delay({x = 0, y = 0}, 0, 0, false), - get_armor_groups = delay({}), - get_attach = delay(), - get_attribute = delay(), -- deprecated - get_bone_position = delay(vector.zero(), vector.zero()), - get_children = delay({}), - get_clouds = delay({ - ambient = { r = 0, b = 0, g = 0, a = 0 }, - color = { r = 0, b = 0, g = 0, a = 0 }, - density = 0, - height = 120, - thickness = 10, - speed = vector.zero(), - }), - get_day_night_ratio = delay(), - get_entity_name = delay(), - get_formspec_prepend = delay(""), - get_fov = delay(0, false, 0), - get_lighting = delay({ - exposure = { - center_weight_power = 1, - exposure_correction = 0, - luminance_max = -3, - luminance_min = -3, - speed_bright_dark = 1000, - speed_dark_bright = 1000, - }, - saturation = 1, - shadows = { - intensity = .6212, - }, - }), - get_local_animation = delay({x = 0, y = 0}, {x = 0, y = 0}, {x = 0, y = 0}, {x = 0, y = 0}, 30), - get_luaentity = delay(), - get_meta = delay({ - contains = delay(false), - get = delay(), - set_string = delay(), - get_string = delay(""), - set_int = delay(), - get_int = delay(0), - set_float = delay(), - get_float = delay(0), - get_keys = delay({}), - to_table = delay({fields = {}}), - from_table = delay(false), - equals = delay(false), - }), - get_moon = delay({ - scale = 1, - texture = "", - tonemap = "", - visible = false, - }), - get_physics_override = delay({ - acceleration_air = 1, - acceleration_default = 1, - gravity = 1, - jump = 1, - liquid_fluidity = 1, - liquid_fluidity_smooth = 1, - liquid_sink = 1, - new_move = true, - sneak = true, - sneak_glitch = false, - speed = 1, - speed_climb = 1, - speed_crouch = 1, - }), - get_player_velocity = vector.zero, -- deprecated - get_rotation = delay(), -- no-op for players - get_sky = delay({ r = 0, g = 0, b = 0, a = 0 }, "regular", {}, true), - get_sky_color = delay({ - dawn_horizon = { r = 0, g = 0, b = 0, a = 0 }, - dawn_sky = { r = 0, g = 0, b = 0, a = 0 }, - day_horizon = { r = 0, g = 0, b = 0, a = 0 }, - day_sky = { r = 0, g = 0, b = 0, a = 0 }, - fog_moon_tint = { r = 0, g = 0, b = 0, a = 0 }, - fog_sun_tint = { r = 0, g = 0, b = 0, a = 0 }, - fog_tint_type = "default", - indoors = { r = 0, g = 0, b = 0, a = 0 }, - night_horizon = { r = 0, g = 0, b = 0, a = 0 }, - night_sky = { r = 0, g = 0, b = 0, a = 0 }, - }), - get_stars = delay({ - count = 1000, - day_opacity = 0, - scale = 1, - star_color = { r = 0, g = 0, b = 0, a = 0 }, - visible = true, - }), - get_sun = delay({ - scale = 1, - sunrise = "", - sunrise_visible = true, - texture = "", - tonemap = "", - visible = true, - }), - get_texture_mod = delay(), -- no-op for players - get_velocity = vector.zero, - get_yaw = delay(), -- no-op for players - getacceleration = delay(), -- backward compatibility - getvelocity = vector.zero, -- backward compatibility - getyaw = delay(), -- backward compatibility - hud_add = delay(), - hud_change = delay(), - hud_get = delay(), - hud_get_flags = delay({ - basic_debug = false, - breathbar = false, - chat = false, - crosshair = false, - healthbar = false, - hotbar = false, - minimap = false, - minimap_radar = false, - wielditem = false, - }), - hud_get_hotbar_image = delay(""), - hud_get_hotbar_itemcount = delay(1), - hud_get_hotbar_selected_image = delay(""), - hud_remove = delay(), - hud_set_flags = delay(), - hud_set_hotbar_image = delay(), - hud_set_hotbar_itemcount = delay(), - hud_set_hotbar_selected_image = delay(), - override_day_night_ratio = delay(), - punch = delay(), - remove = delay(), - respawn = delay(), - right_click = delay(), - send_mapblock = delay(), - set_acceleration = delay(), - set_animation = delay(), - set_animation_frame_speed = delay(), - set_armor_groups = delay(), - set_attach = delay(), - set_attribute = delay(), -- deprecated - set_bone_position = delay(), - set_clouds = delay(), - set_detach = delay(), - set_formspec_prepend = delay(), - set_fov = delay(), - set_lighting = delay(), - set_local_animation = delay(), - set_look_horizontal = delay(), - set_look_pitch = delay(), - set_look_vertical = delay(), - set_look_yaw = delay(), - set_minimap_modes = delay(), - set_moon = delay(), - set_nametag_attributes = delay(), - set_physics_override = delay(), - set_rotation = delay(), -- no-op for players - set_sky = delay(), - set_sprite = delay(), -- no-op for players - set_stars = delay(), - set_sun = delay(), - set_texture_mod = delay(), -- no-op for players - set_velocity = delay(), -- no-op for players - set_yaw = delay(), -- no-op for players - setacceleration = delay(), -- backward compatibility - setsprite = delay(), -- backward compatibility - settexturemod = delay(), -- backward compatibility - setvelocity = delay(), -- backward compatibility - setyaw = delay(), -- backward compatibility -} - -function pipeworks.create_fake_player(def, is_dynamic) - local wielded_item = ItemStack("") - if def.inventory and def.wield_list then - wielded_item = def.inventory:get_stack(def.wield_list, def.wield_index or 1) - end - local p = { - get_player_name = delay(def.name), - - _formspec = def.formspec or "", - _hp = def.hp or 20, - _breath = 11, - _pos = def.position and table.copy(def.position) or vector.new(), - _properties = def.properties or { eye_height = def.eye_height or 1.47 }, - _inventory = def.inventory, - _wield_index = def.wield_index or 1, - _wielded_item = wielded_item, - - -- Model and view - _eye_offset1 = vector.new(), - _eye_offset3 = vector.new(), - set_eye_offset = function(self, first, third) - self._eye_offset1 = table.copy(first) - self._eye_offset3 = table.copy(third) - end, - get_eye_offset = function(self) - return self._eye_offset1, self._eye_offset3 - end, - get_look_dir = delay(def.look_dir or vector.new()), - get_look_pitch = delay(def.look_pitch or 0), - get_look_yaw = delay(def.look_yaw or 0), - get_look_horizontal = delay(def.look_yaw or 0), - get_look_vertical = delay(-(def.look_pitch or 0)), - - -- Controls - get_player_control = delay({ - jump=false, right=false, left=false, LMB=false, RMB=false, - sneak=def.sneak, aux1=false, down=false, up=false - }), - get_player_control_bits = delay(def.sneak and 64 or 0), - - -- Inventory and ItemStacks - get_inventory = delay(def.inventory), - set_wielded_item = function(self, item) - if self._inventory and def.wield_list then - return self._inventory:set_stack(def.wield_list, - self._wield_index, item) - end - self._wielded_item = ItemStack(item) - end, - get_wielded_item = function(self, item) - if self._inventory and def.wield_list then - return self._inventory:get_stack(def.wield_list, - self._wield_index) - end - return ItemStack(self._wielded_item) - end, - get_wield_list = delay(def.wield_list), - get_nametag_attributes = delay({ - bgcolor = false, - color = { r = 0, g = 0, b = 0, a = 0 }, - text = def.name, - }), - } - -- Getter & setter functions - p.get_inventory_formspec, p.set_inventory_formspec - = get_set_wrap("formspec", is_dynamic) - p.get_breath, p.set_breath = get_set_wrap("breath", is_dynamic) - p.get_hp, p.set_hp = get_set_wrap("hp", is_dynamic) - p.get_pos, p.set_pos = get_set_wrap("pos", is_dynamic) - p.get_wield_index, p.set_wield_index = get_set_wrap("wield_index", true) - p.get_properties, p.set_properties = get_set_wrap("properties", false) - - -- For players, move_to and get_pos do the same - p.move_to = p.get_pos - - -- Backwards compatibility - p.getpos = p.get_pos - p.setpos = p.set_pos - p.moveto = p.move_to - setmetatable(p, { __index = fake_player_metatable }) - return p -end +-- Kept for compatibility with old mods +pipeworks.create_fake_player = fakelib.create_player diff --git a/filter-injector.lua b/filter-injector.lua index 21b3aca..293a066 100644 --- a/filter-injector.lua +++ b/filter-injector.lua @@ -84,9 +84,7 @@ local function punch_filter(data, filtpos, filtnode, msg) local filtmeta = minetest.get_meta(filtpos) local filtinv = filtmeta:get_inventory() local owner = filtmeta:get_string("owner") - local fakePlayer = pipeworks.create_fake_player({ - name = owner - }) + local fakeplayer = fakelib.create_player(owner) local dir = pipeworks.facedir_to_right_dir(filtnode.param2) local frompos = vector.subtract(filtpos, dir) local fromnode = minetest.get_node(frompos) @@ -322,7 +320,7 @@ local function punch_filter(data, filtpos, filtnode, msg) if fromtube.can_remove then doRemove = fromtube.can_remove(frompos, fromnode, stack, dir, frominvname, spos) elseif fromdef.allow_metadata_inventory_take then - doRemove = fromdef.allow_metadata_inventory_take(frompos, frominvname,spos, stack, fakePlayer) + doRemove = fromdef.allow_metadata_inventory_take(frompos, frominvname, spos, stack, fakeplayer) end -- stupid lack of continue statements grumble if doRemove > 0 then @@ -356,13 +354,13 @@ local function punch_filter(data, filtpos, filtnode, msg) item = stack:take_item(count) frominv:set_stack(frominvname, spos, stack) if fromdef.on_metadata_inventory_take then - fromdef.on_metadata_inventory_take(frompos, frominvname, spos, item, fakePlayer) + fromdef.on_metadata_inventory_take(frompos, frominvname, spos, item, fakeplayer) end end local pos = vector.add(frompos, vector.multiply(dir, 1.4)) local start_pos = vector.add(frompos, dir) pipeworks.tube_inject_item(pos, start_pos, dir, item, - fakePlayer:get_player_name(), item_tags) + fakeplayer:get_player_name(), item_tags) return true -- only fire one item, please end end diff --git a/legacy.lua b/legacy.lua deleted file mode 100644 index 743d6f1..0000000 --- a/legacy.lua +++ /dev/null @@ -1,62 +0,0 @@ -local S = minetest.get_translator("pipeworks") - -if not minetest.get_modpath("auto_tree_tap") and - minetest.get_modpath("technic") then - - minetest.register_abm({ - nodenames = { "auto_tree_tap:off", "auto_tree_tap:on" }, - chance = 1, - interval = 1, - action = function(pos, node, active_object_count, active_object_count_wider) - local fdir = node.param2 - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - inv:set_size("pick", 1) - inv:set_size("ghost_pick", 1) - inv:set_size("main", 100) - minetest.set_node(pos, {name = "pipeworks:nodebreaker_off", param2 = fdir}) - minetest.registered_nodes["pipeworks:nodebreaker_off"].on_punch(pos, node) - inv:set_stack("pick", 1, ItemStack("technic:treetap")) - end - }) - - minetest.register_node(":auto_tree_tap:off", { - description = S("Auto-Tap"), - tiles = {"pipeworks_nodebreaker_top_off.png","pipeworks_nodebreaker_bottom_off.png","pipeworks_nodebreaker_side2_off.png","pipeworks_nodebreaker_side1_off.png", - "pipeworks_nodebreaker_back.png","pipeworks_nodebreaker_front_off.png"}, - is_ground_content = false, - paramtype2 = "facedir", - groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2, mesecon = 2,tubedevice=1, not_in_creative_inventory=1, axey=1, handy=1, pickaxey=1}, - _mcl_hardness=0.8, - _sound_def = { - key = "node_sound_stone_defaults", - }, - tube = {connect_sides={back=1}}, - on_construct = function(pos) - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - inv:set_size("pick", 1) - inv:set_stack("pick", 1, ItemStack("default:pick_mese")) - end, - after_place_node = function (pos, placer) - pipeworks.scan_for_tube_objects(pos, placer) - local placer_pos = placer:get_pos() - - --correct for the player's height - if placer:is_player() then placer_pos.y = placer_pos.y + 1.5 end - - --correct for 6d facedir - if placer_pos then - local dir = { - x = pos.x - placer_pos.x, - y = pos.y - placer_pos.y, - z = pos.z - placer_pos.z - } - local node = minetest.get_node(pos) - node.param2 = minetest.dir_to_facedir(dir, true) - minetest.set_node(pos, node) - end - end, - after_dig_node = pipeworks.scan_for_tube_objects, - }) -end diff --git a/mod.conf b/mod.conf index 479abc8..ea1a06d 100644 --- a/mod.conf +++ b/mod.conf @@ -1,5 +1,5 @@ name = pipeworks description = This mod uses mesh nodes and nodeboxes to supply a complete set of 3D pipes and tubes, along with devices that work with them. -depends = basic_materials, xcompat +depends = basic_materials, xcompat, fakelib optional_depends = mesecons, mesecons_mvps, digilines, signs_lib, unified_inventory, default, screwdriver, fl_mapgen, sound_api, i3, hades_core, hades_furnaces, hades_chests, mcl_mapgen_core, mcl_barrels, mcl_furnaces, mcl_experience min_minetest_version = 5.5.0 diff --git a/wielder.lua b/wielder.lua index 44121ba..9050490 100644 --- a/wielder.lua +++ b/wielder.lua @@ -1,247 +1,201 @@ local S = minetest.get_translator("pipeworks") -local assumed_eye_pos = vector.new(0, 1.5, 0) +local has_digilines = minetest.get_modpath("digilines") -local function delay(x) - return (function() return x end) -end - -local function set_wielder_formspec(data, meta) - local size = "10.2,"..(7+data.wield_inv_height) - local list_background = "" +local function set_wielder_formspec(def, meta) + local width, height = def.wield_inv.width, def.wield_inv.height + local offset = 5.22 - width * 0.625 + local size = "10.2,"..(6.5 + height * 1.25 + (has_digilines and 1.25 or 0)) + local list_bg = "" if minetest.get_modpath("i3") or minetest.get_modpath("mcl_formspec") then - list_background = "style_type[box;colors=#666]" - for i=0, data.wield_inv_height-1 do - for j=0, data.wield_inv_width-1 do - list_background = list_background .. "box[".. ((10-data.wield_inv_width)*0.5)+(i*1.25) ..",".. 1+(j*1.25) ..";1,1;]" + list_bg = "style_type[box;colors=#666]" + for i=0, height-1 do + for j=0, width-1 do + list_bg = list_bg.."box["..offset+(i*1.25)..","..1.25+(j*1.25)..";1,1;]" end end end - meta:set_string("formspec", - "formspec_version[2]" .. - "size["..size.."]".. - pipeworks.fs_helpers.get_prepends(size).. - "item_image[0.5,0.5;1,1;"..data.name_base.."_off]".. - "label[1.5,1;"..minetest.formspec_escape(data.description).."]".. - list_background .. - "list[context;"..minetest.formspec_escape(data.wield_inv_name)..";"..((10-data.wield_inv_width)*0.5)..",1;"..data.wield_inv_width..","..data.wield_inv_height..";]".. - pipeworks.fs_helpers.get_inv((2+data.wield_inv_height)) .. - "listring[context;"..minetest.formspec_escape(data.wield_inv_name).."]" .. - "listring[current_player;main]" - ) - meta:set_string("infotext", data.description) -end - -local can_tool_dig_node = function(nodename, toolcaps, toolname) - --pipeworks.logger("can_tool_dig_node() STUB nodename="..tostring(nodename).." toolname="..tostring(toolname).." toolcaps: "..dump(toolcaps)) - -- brief documentation of minetest.get_dig_params() as it's not yet documented in lua_api.txt: - -- takes two arguments, a node's block groups and a tool's capabilities, - -- both as they appear in their respective definitions. - -- returns a table with the following fields: - -- diggable: boolean, can this tool dig this node at all - -- time: float, time needed to dig with this tool - -- wear: int, number of wear points to inflict on the item - local nodedef = minetest.registered_nodes[nodename] - -- don't explode due to nil def in event of unknown node! - if (nodedef == nil) then return false end - - local nodegroups = nodedef.groups - local diggable = minetest.get_dig_params(nodegroups, toolcaps).diggable - if not diggable then - -- a pickaxe can't actually dig leaves based on it's groups alone, - -- but a player holding one can - the game seems to fall back to the hand. - -- fall back to checking the hand's properties if the tool isn't the correct one. - local hand_caps = minetest.registered_items[""].tool_capabilities - diggable = minetest.get_dig_params(nodegroups, hand_caps).diggable + local inv_offset = 1.5 + height * 1.25 + local fs = "formspec_version[2]size["..size.."]".. + pipeworks.fs_helpers.get_prepends(size)..list_bg.. + "item_image[0.5,0.3;1,1;"..def.name.."_off]".. + "label[1.75,0.8;"..minetest.formspec_escape(def.description).."]".. + "list[context;"..def.wield_inv.name..";"..offset..",1.25;"..width..","..height..";]" + if has_digilines then + fs = fs.."field[1.5,"..inv_offset..";5,0.8;channel;"..S("Channel")..";${channel}]".. + "button_exit[6.5,"..inv_offset..";2,0.8;save;"..S("Save").."]".. + pipeworks.fs_helpers.get_inv(inv_offset + 1.25).."listring[]" + else + fs = fs..pipeworks.fs_helpers.get_inv(inv_offset).."listring[]" end - return diggable + meta:set_string("formspec", fs) + meta:set_string("infotext", def.description) end -local function wielder_on(data, wielder_pos, wielder_node) - data.fixup_node(wielder_pos, wielder_node) - if wielder_node.name ~= data.name_base.."_off" then return end - wielder_node.name = data.name_base.."_on" - minetest.swap_node(wielder_pos, wielder_node) - minetest.check_for_falling(wielder_pos) - local wielder_meta = minetest.get_meta(wielder_pos) - local inv = wielder_meta:get_inventory() - local wield_inv_name = data.wield_inv_name - local wieldindex - for i, stack in ipairs(inv:get_list(wield_inv_name)) do - if not stack:is_empty() then - wieldindex = i - break +local function wielder_action(def, pos, node, index) + local meta = minetest.get_meta(pos) + local inv = meta:get_inventory() + local list = inv:get_list(def.wield_inv.name) + local wield_index + if index then + if list[index] and (def.wield_hand or not list[index]:is_empty()) then + wield_index = index + end + else + for i, stack in ipairs(list) do + if not stack:is_empty() then + wield_index = i + break + end end end - if not wieldindex then - if not data.ghost_inv_name then return end - wield_inv_name = data.ghost_inv_name - inv:set_stack(wield_inv_name, 1, ItemStack(data.ghost_tool)) - wieldindex = 1 + if not wield_index and not def.wield_hand then + return end - local dir = minetest.facedir_to_dir(wielder_node.param2) - -- under/above is currently intentionally left switched - -- even though this causes some problems with deployers and e.g. seeds - -- as there are some issues related to nodebreakers otherwise breaking 2 nodes afar. - -- solidity would have to be checked as well, - -- but would open a whole can of worms related to difference in nodebreaker/deployer behavior - -- and the problems of wielders acting on themselves if below is solid - local under_pos = vector.subtract(wielder_pos, dir) - local above_pos = vector.subtract(under_pos, dir) - local pitch - local yaw - if dir.z < 0 then - yaw = 0 - pitch = 0 - elseif dir.z > 0 then - yaw = math.pi - pitch = 0 - elseif dir.x < 0 then - yaw = 3*math.pi/2 - pitch = 0 - elseif dir.x > 0 then - yaw = math.pi/2 - pitch = 0 - elseif dir.y > 0 then - yaw = 0 - pitch = -math.pi/2 - else - yaw = 0 - pitch = math.pi/2 - end - local virtplayer = pipeworks.create_fake_player({ - name = data.masquerade_as_owner and wielder_meta:get_string("owner") - or ":pipeworks:" .. minetest.pos_to_string(wielder_pos), - formspec = wielder_meta:get_string("formspec"), - look_dir = vector.multiply(dir, -1), - look_pitch = pitch, - look_yaw = yaw, - sneak = data.sneak, - position = vector.subtract(wielder_pos, assumed_eye_pos), + local dir = minetest.facedir_to_dir(node.param2) + local fakeplayer = fakelib.create_player({ + name = meta:get_string("owner"), + direction = vector.multiply(dir, -1), + position = pos, inventory = inv, - wield_index = wieldindex, - wield_list = wield_inv_name + wield_index = wield_index or 1, + wield_list = def.wield_inv.name, }) - - local pointed_thing = { type="node", under=under_pos, above=above_pos } - data.act(virtplayer, pointed_thing) - if data.eject_drops then + -- Under and above positions are intentionally switched. + local pointed = { + type = "node", + under = vector.subtract(pos, dir), + above = vector.subtract(pos, vector.multiply(dir, 2)), + } + def.action(fakeplayer, pointed) + if def.eject_drops then for i, stack in ipairs(inv:get_list("main")) do if not stack:is_empty() then - pipeworks.tube_inject_item(wielder_pos, wielder_pos, dir, stack) + pipeworks.tube_inject_item(pos, pos, dir, stack) inv:set_stack("main", i, ItemStack("")) end end end end -local function wielder_off(data, pos, node) - if node.name == data.name_base.."_on" then - node.name = data.name_base.."_off" +local function wielder_on(def, pos, node) + if node.name ~= def.name.."_off" then + return + end + node.name = def.name.."_on" + minetest.swap_node(pos, node) + wielder_action(def, pos, node) +end + +local function wielder_off(def, pos, node) + if node.name == def.name.."_on" then + node.name = def.name.."_off" minetest.swap_node(pos, node) - minetest.check_for_falling(pos) end end -local function register_wielder(data) - data.fixup_node = data.fixup_node or function (pos, node) end - data.fixup_oldmetadata = data.fixup_oldmetadata or function (m) return m end - for _, state in ipairs({ "off", "on" }) do - local groups = { snappy=2, choppy=2, oddly_breakable_by_hand=2, mesecon=2, tubedevice=1, tubedevice_receiver=1, axey=1, handy=1, pickaxey=1 } - if state == "on" then groups.not_in_creative_inventory = 1 end - local tile_images = {} - for _, face in ipairs({ "top", "bottom", "side2", "side1", "back", "front" }) do - table.insert(tile_images, data.texture_base.."_"..face..(data.texture_stateful[face] and "_"..state or "")..".png") +local function wielder_digiline_action(def, pos, channel, msg) + local meta = minetest.get_meta(pos) + local set_channel = meta:get_string("channel") + if channel ~= set_channel then + return + end + if type(msg) ~= "table" then + if type(msg) == "string" then + if msg:sub(1, 8) == "activate" then + msg = {command = "activate", slot = tonumber(msg:sub(9))} + end + else + return end - minetest.register_node(data.name_base.."_"..state, { - description = data.description, - tiles = tile_images, + end + if msg.command == "activate" then + local node = minetest.get_node(pos) + local index = type(msg.slot) == "number" and msg.slot or nil + wielder_action(def, pos, node, index) + end +end + +function pipeworks.register_wielder(def) + for _,state in ipairs({"off", "on"}) do + local groups = { + snappy = 2, choppy = 2, oddly_breakable_by_hand = 2, + mesecon = 2, tubedevice = 1, tubedevice_receiver = 1, + axey = 1, handy = 1, pickaxey = 1, + not_in_creative_inventory = state == "on" and 1 or nil + } + minetest.register_node(def.name.."_"..state, { + description = def.description, + tiles = def.tiles[state], + paramtype2 = "facedir", + groups = groups, + is_ground_content = false, + _mcl_hardness = 0.6, + _sound_def = { + key = "node_sound_stone_defaults", + }, + drop = def.name.."_off", mesecons = { effector = { rules = pipeworks.rules_all, - action_on = function (pos, node) - wielder_on(data, pos, node) + action_on = function(pos, node) + wielder_on(def, pos, node) end, - action_off = function (pos, node) - wielder_off(data, pos, node) + action_off = function(pos, node) + wielder_off(def, pos, node) + end, + }, + }, + digilines = { + receptor = {}, + effector = { + action = function(pos, _, channel, msg) + wielder_digiline_action(def, pos, channel, msg) end, }, }, tube = { - can_insert = function(pos, node, stack, tubedir) - if not data.tube_permit_anteroposterior_insert then - local nodedir = minetest.facedir_to_dir(node.param2) - if vector.equals(tubedir, nodedir) or vector.equals(tubedir, vector.multiply(nodedir, -1)) then + can_insert = function(pos, node, stack, direction) + if def.eject_drops then + -- Prevent ejected items from being inserted + local dir = vector.multiply(minetest.facedir_to_dir(node.param2), -1) + if vector.equals(direction, dir) then return false end end - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - return inv:room_for_item(data.wield_inv_name, stack) + local inv = minetest.get_meta(pos):get_inventory() + return inv:room_for_item(def.wield_inv.name, stack) end, - insert_object = function(pos, node, stack, tubedir) - if not data.tube_permit_anteroposterior_insert then - local nodedir = minetest.facedir_to_dir(node.param2) - if vector.equals(tubedir, nodedir) or vector.equals(tubedir, vector.multiply(nodedir, -1)) then - return stack - end - end - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - return inv:add_item(data.wield_inv_name, stack) + insert_object = function(pos, node, stack) + local inv = minetest.get_meta(pos):get_inventory() + return inv:add_item(def.wield_inv.name, stack) end, - input_inventory = data.wield_inv_name, - connect_sides = data.tube_connect_sides, - can_remove = function(pos, node, stack, tubedir) + input_inventory = "main", + connect_sides = def.connect_sides, + can_remove = function(pos, node, stack) return stack:get_count() end, }, - is_ground_content = false, - paramtype2 = "facedir", - tubelike = 1, - groups = groups, - _mcl_hardness=0.6, - _sound_def = { - key = "node_sound_stone_defaults", - }, - drop = data.name_base.."_off", on_construct = function(pos) local meta = minetest.get_meta(pos) - set_wielder_formspec(data, meta) local inv = meta:get_inventory() - inv:set_size(data.wield_inv_name, data.wield_inv_width*data.wield_inv_height) - if data.ghost_inv_name then - inv:set_size(data.ghost_inv_name, 1) - end - if data.eject_drops then - inv:set_size("main", 100) + inv:set_size(def.wield_inv.name, def.wield_inv.width * def.wield_inv.height) + if def.eject_drops then + inv:set_size("main", 32) end + set_wielder_formspec(def, meta) end, - after_place_node = function (pos, placer) + after_place_node = function(pos, placer) pipeworks.scan_for_tube_objects(pos) - local placer_pos = placer:get_pos() - if placer_pos and placer:is_player() then placer_pos = vector.add(placer_pos, assumed_eye_pos) end - if placer_pos then - local dir = vector.subtract(pos, placer_pos) - local node = minetest.get_node(pos) - node.param2 = minetest.dir_to_facedir(dir, true) - minetest.set_node(pos, node) + if not placer then + return end + local node = minetest.get_node(pos) + node.param2 = minetest.dir_to_facedir(placer:get_look_dir(), true) + minetest.set_node(pos, node) minetest.get_meta(pos):set_string("owner", placer:get_player_name()) end, - can_dig = (data.can_dig_nonempty_wield_inv and delay(true) or function(pos, player) - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - return inv:is_empty(data.wield_inv_name) - end), after_dig_node = function(pos, oldnode, oldmetadata, digger) - -- The legacy-node fixup is done here in a - -- different form from the standard fixup, - -- rather than relying on a standard fixup - -- in an on_dig callback, because some - -- non-standard diggers (such as technic's - -- mining drill) don't respect on_dig. - oldmetadata = data.fixup_oldmetadata(oldmetadata) - for _, stack in ipairs(oldmetadata.inventory[data.wield_inv_name] or {}) do + for _,stack in ipairs(oldmetadata.inventory.main or {}) do if not stack:is_empty() then minetest.add_item(pos, stack) end @@ -249,7 +203,6 @@ local function register_wielder(data) pipeworks.scan_for_tube_objects(pos) end, on_rotate = pipeworks.on_rotate, - on_punch = data.fixup_node, allow_metadata_inventory_put = function(pos, listname, index, stack, player) if not pipeworks.may_configure(pos, player) then return 0 end return stack:get_count() @@ -261,212 +214,141 @@ local function register_wielder(data) allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player) if not pipeworks.may_configure(pos, player) then return 0 end return count - end + end, + on_receive_fields = function(pos, _, fields, sender) + if not fields.channel or not pipeworks.may_configure(pos, sender) then + return + end + minetest.get_meta(pos):set_string("channel", fields.channel) + end, }) end + table.insert(pipeworks.ui_cat_tube_list, def.name.."_off") +end + +local function get_tiles(name, stateful) + local tiles = {on = {}, off = {}} + for _,state in ipairs({"off", "on"}) do + for _,side in ipairs({"top", "bottom", "side2", "side1", "back", "front"}) do + local suffix = stateful[side] and "_"..state or "" + table.insert(tiles[state], "pipeworks_"..name.."_"..side..suffix..".png") + end + end + return tiles end if pipeworks.enable_node_breaker then - local data - -- see after end of data table for other use of these variables - local name_base = "pipeworks:nodebreaker" - local wield_inv_name = "pick" - data = { - name_base = name_base, + pipeworks.register_wielder({ + name = "pipeworks:nodebreaker", description = S("Node Breaker"), - texture_base = "pipeworks_nodebreaker", - texture_stateful = { top = true, bottom = true, side2 = true, side1 = true, front = true }, - tube_connect_sides = { top=1, bottom=1, left=1, right=1, back=1 }, - tube_permit_anteroposterior_insert = false, - wield_inv_name = wield_inv_name, - wield_inv_width = 1, - wield_inv_height = 1, - can_dig_nonempty_wield_inv = true, - ghost_inv_name = "ghost_pick", - ghost_tool = ":", -- hand by default - fixup_node = function (pos, node) - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - -- Node breakers predating the visible pick slot - -- may have been partially updated. This code - -- fully updates them. Some have been observed - -- to have no pick slot at all; first add one. - if inv:get_size("pick") ~= 1 then - inv:set_size("pick", 1) - end - -- Originally, they had a ghost pick in a "pick" - -- inventory, no other inventory, and no form. - -- The partial update of early with-form node - -- breaker code gives them "ghost_pick" and "main" - -- inventories, but leaves the old ghost pick in - -- the "pick" inventory, and doesn't add a form. - -- First perform that partial update. - if inv:get_size("ghost_pick") ~= 1 then - inv:set_size("ghost_pick", 1) - inv:set_size("main", 100) - end - -- If the node breaker predates the visible pick - -- slot, which we can detect by it not having a - -- form, then the pick slot needs to be cleared - -- of the old ghost pick. - if (meta:get_string("formspec") or "") == "" then - inv:set_stack("pick", 1, ItemStack("")) - end - -- Finally, unconditionally set the formspec - -- and infotext. This not only makes the - -- pick slot visible for node breakers where - -- it wasn't before; it also updates the form - -- for node breakers that had an older version - -- of the form, and sets infotext where it was - -- missing for early with-form node breakers. - set_wielder_formspec(data, meta) - end, - fixup_oldmetadata = function (oldmetadata) - -- Node breakers predating the visible pick slot, - -- with node form, kept their ghost pick in an - -- inventory named "pick", the same name as the - -- later visible pick slot. The pick must be - -- removed to avoid spilling it. - if not oldmetadata.fields.formspec then - return { inventory = { pick = {} }, fields = oldmetadata.fields } + tiles = get_tiles("nodebreaker", {top = 1, bottom = 1, side2 = 1, side1 = 1, front = 1}), + connect_sides = {top = 1, bottom = 1, left = 1, right = 1, back = 1}, + wield_inv = {name = "pick", width = 1, height = 1}, + wield_hand = true, + eject_drops = true, + action = function(fakeplayer, pointed) + local stack = fakeplayer:get_wielded_item() + local old_stack = ItemStack(stack) + local item_def = minetest.registered_items[stack:get_name()] + if item_def.on_use then + fakeplayer:set_wielded_item(item_def.on_use(stack, fakeplayer, pointed) or stack) else - return oldmetadata - end - end, - masquerade_as_owner = true, - sneak = false, - act = function(virtplayer, pointed_thing) - if minetest.is_protected(vector.add(virtplayer:get_pos(), assumed_eye_pos), virtplayer:get_player_name()) then - return - end - - --local dname = "nodebreaker.act() " - local wieldstack = virtplayer:get_wielded_item() - local oldwieldstack = ItemStack(wieldstack) - local on_use = (minetest.registered_items[wieldstack:get_name()] or {}).on_use - if on_use then - --pipeworks.logger(dname.."invoking on_use "..tostring(on_use)) - wieldstack = on_use(wieldstack, virtplayer, pointed_thing) or wieldstack - virtplayer:set_wielded_item(wieldstack) - else - local under_node = minetest.get_node(pointed_thing.under) - local def = minetest.registered_nodes[under_node.name] - if not def then - -- do not dig an unknown node + local node = minetest.get_node(pointed.under) + local node_def = minetest.registered_nodes[node.name] + if not node_def or not node_def.on_dig then return end - -- check that the current tool is capable of destroying the - -- target node. - -- if we can't, don't dig, and leave the wield stack unchanged. - -- note that wieldstack:get_tool_capabilities() returns hand - -- properties if the item has none of it's own. - if can_tool_dig_node(under_node.name, - wieldstack:get_tool_capabilities(), - wieldstack:get_name()) then - def.on_dig(pointed_thing.under, under_node, virtplayer) - local sound = def.sounds and def.sounds.dug - if sound then - minetest.sound_play(sound.name, - {pos=pointed_thing.under, gain=sound.gain}) + -- Check if the tool can dig the node + local tool = stack:get_tool_capabilities() + if not minetest.get_dig_params(node_def.groups, tool).diggable then + -- Try using hand if tool can't dig the node + local hand = ItemStack():get_tool_capabilities() + if not minetest.get_dig_params(node_def.groups, hand).diggable then + return end - wieldstack = virtplayer:get_wielded_item() - --~ else - --pipeworks.logger(dname.."couldn't dig node!") end + -- This must only check for false, because `on_dig` returning nil is the same as returning true. + if node_def.on_dig(pointed.under, node, fakeplayer) == false then + return + end + local sound = node_def.sounds and node_def.sounds.dug + if sound then + minetest.sound_play(sound, {pos = pointed.under}, true) + end + stack = fakeplayer:get_wielded_item() end - local wieldname = wieldstack:get_name() - if wieldname == oldwieldstack:get_name() then - -- don't mechanically wear out tool - if wieldstack:get_count() == oldwieldstack:get_count() and - wieldstack:get_metadata() == oldwieldstack:get_metadata() and - ((minetest.registered_items[wieldstack:get_name()] or {}).wear_represents or "mechanical_wear") == "mechanical_wear" then - virtplayer:set_wielded_item(oldwieldstack) + if stack:get_name() == old_stack:get_name() then + -- Don't mechanically wear out tool + if stack:get_wear() ~= old_stack:get_wear() and stack:get_count() == old_stack:get_count() + and (item_def.wear_represents == nil or item_def.wear_represents == "mechanical_wear") then + print("replaced") + fakeplayer:set_wielded_item(old_stack) end - elseif wieldname ~= "" then - -- tool got replaced by something else: - -- treat it as a drop - virtplayer:get_inventory():add_item("main", wieldstack) - virtplayer:set_wielded_item(ItemStack("")) + elseif not stack:is_empty() then + -- Tool got replaced by something else, treat it as a drop. + fakeplayer:get_inventory():add_item("main", stack) + fakeplayer:set_wielded_item("") end end, - eject_drops = true, - } - register_wielder(data) - pipeworks.ui_cat_tube_list[#pipeworks.ui_cat_tube_list+1] = "pipeworks:nodebreaker_off" - -- aliases for when someone had technic installed, but then uninstalled it but not pipeworks + }) minetest.register_alias("technic:nodebreaker_off", "pipeworks:nodebreaker_off") minetest.register_alias("technic:nodebreaker_on", "pipeworks:nodebreaker_on") minetest.register_alias("technic:node_breaker_off", "pipeworks:nodebreaker_off") minetest.register_alias("technic:node_breaker_on", "pipeworks:nodebreaker_on") - -- turn legacy auto-tree-taps into node breakers - dofile(pipeworks.modpath.."/legacy.lua") - - -- register LBM for transition to cheaper node breakers - local lbm_id = "pipeworks:refund_node_breaker_pick" - minetest.register_lbm({ - name = lbm_id, - label = "Give back mese pick for pre-transition node breakers", - run_at_every_load = false, - nodenames = { name_base.."_on", name_base.."_off" }, - action = function(pos, node) - pipeworks.logger(lbm_id.." entry, nodename="..node.name) - local invref = minetest.get_meta(pos):get_inventory() - invref:add_item(wield_inv_name, ItemStack("default:pick_mese")) - end - }) + minetest.register_alias("auto_tree_tap:off", "pipeworks:nodebreaker_off") + minetest.register_alias("auto_tree_tap:on", "pipeworks:nodebreaker_on") end if pipeworks.enable_deployer then - register_wielder({ - name_base = "pipeworks:deployer", + pipeworks.register_wielder({ + name = "pipeworks:deployer", description = S("Deployer"), - texture_base = "pipeworks_deployer", - texture_stateful = { front = true }, - tube_connect_sides = { back=1 }, - tube_permit_anteroposterior_insert = true, - wield_inv_name = "main", - wield_inv_width = 3, - wield_inv_height = 3, - can_dig_nonempty_wield_inv = false, - masquerade_as_owner = true, - sneak = false, - act = function(virtplayer, pointed_thing) - if minetest.is_protected(vector.add(virtplayer:get_pos(), assumed_eye_pos), virtplayer:get_player_name()) then - return + tiles = get_tiles("deployer", {front = 1}), + connect_sides = {back = 1}, + wield_inv = {name = "main", width = 3, height = 3}, + action = function(fakeplayer, pointed) + local stack = fakeplayer:get_wielded_item() + local def = minetest.registered_items[stack:get_name()] + if def and def.on_place then + local new_stack, placed_pos = def.on_place(stack, fakeplayer, pointed) + fakeplayer:set_wielded_item(new_stack or stack) + -- minetest.item_place_node doesn't play sound to the placer + local sound = placed_pos and def.sounds and def.sounds.place + local name = fakeplayer:get_player_name() + if sound and name ~= "" then + minetest.sound_play(sound, {pos = placed_pos, to_player = name}, true) + end end - - local wieldstack = virtplayer:get_wielded_item() - virtplayer:set_wielded_item((minetest.registered_items[wieldstack:get_name()] or {on_place=minetest.item_place}).on_place(wieldstack, virtplayer, pointed_thing) or wieldstack) end, - eject_drops = false, }) - pipeworks.ui_cat_tube_list[#pipeworks.ui_cat_tube_list+1] = "pipeworks:deployer_off" - -- aliases for when someone had technic installed, but then uninstalled it but not pipeworks minetest.register_alias("technic:deployer_off", "pipeworks:deployer_off") minetest.register_alias("technic:deployer_on", "pipeworks:deployer_on") end if pipeworks.enable_dispenser then - register_wielder({ - name_base = "pipeworks:dispenser", + -- Override minetest.item_drop to negate its hardcoded offset + -- when the dropper is a fake player. + local item_drop = minetest.item_drop + -- luacheck: ignore 122 + function minetest.item_drop(stack, dropper, pos) + if dropper and dropper.is_fake_player then + pos = vector.new(pos.x, pos.y - 1.2, pos.z) + end + return item_drop(stack, dropper, pos) + end + pipeworks.register_wielder({ + name = "pipeworks:dispenser", description = S("Dispenser"), - texture_base = "pipeworks_dispenser", - texture_stateful = { front = true }, - tube_connect_sides = { back=1 }, - tube_permit_anteroposterior_insert = true, - wield_inv_name = "main", - wield_inv_width = 3, - wield_inv_height = 3, - can_dig_nonempty_wield_inv = false, - masquerade_as_owner = false, - sneak = true, - act = function(virtplayer, pointed_thing) - local wieldstack = virtplayer:get_wielded_item() - virtplayer:set_wielded_item((minetest.registered_items[wieldstack:get_name()] or - {on_drop=minetest.item_drop}).on_drop(wieldstack, virtplayer, virtplayer:get_pos()) or - wieldstack) + tiles = get_tiles("dispenser", {front = 1}), + connect_sides = {back = 1}, + wield_inv = {name = "main", width = 3, height = 3}, + action = function(fakeplayer) + local stack = fakeplayer:get_wielded_item() + local def = minetest.registered_items[stack:get_name()] + if def and def.on_drop then + local pos = fakeplayer:get_pos() + fakeplayer:set_wielded_item(def.on_drop(stack, fakeplayer, pos) or stack) + end end, - eject_drops = false, }) - pipeworks.ui_cat_tube_list[#pipeworks.ui_cat_tube_list+1] = "pipeworks:dispenser_off" end