From 0375cb1480cab954167429a6bd156a6bea7ce459 Mon Sep 17 00:00:00 2001 From: Thomas--S Date: Sat, 12 Sep 2020 09:12:45 +0200 Subject: [PATCH] Quarry: Improve digging behaviour Quarry now digs nodes like a player. Additionally, some possible causes for crashes are fixed. E.g. the `inv` in the `after_dig_node` callbacks were not guaranteed to exist. Please note that TA4 batteries that were dug before this change are considered as full. --- basic_machines/quarry.lua | 61 ++++++++++-------- basis/fake_player.lua | 118 ++++++++++++++++++++++++++++++++++ basis/lib.lua | 39 +++++++++++- digtron/battery.lua | 2 +- icta_controller/battery.lua | 124 +++++++++++++++++------------------- init.lua | 1 + ta3_power/akkubox.lua | 17 ++--- 7 files changed, 257 insertions(+), 105 deletions(-) create mode 100644 basis/fake_player.lua diff --git a/basic_machines/quarry.lua b/basic_machines/quarry.lua index c7e6df1..ab938c7 100644 --- a/basic_machines/quarry.lua +++ b/basic_machines/quarry.lua @@ -157,23 +157,6 @@ local function mark_area(pos1, pos2, owner) pos1.y = pos1.y - 0.2 end -local function peek_node(qpos) - local node = techage.get_node_lvm(qpos) - local ndef = minetest.registered_nodes[node.name] - if techage.can_node_dig(node, ndef) then - return techage.dropped_node(node, ndef) - end -end - -local function add_to_inv(pos, item_name) - local inv = M(pos):get_inventory() - if inv:room_for_item("main", item_name) then - inv:add_item("main", item_name) - return true - end - return false -end - local function quarry_task(pos, crd, nvm) nvm.start_level = nvm.start_level or 0 nvm.quarry_depth = nvm.quarry_depth or 1 @@ -182,6 +165,30 @@ local function quarry_task(pos, crd, nvm) local y_last = y_first - nvm.quarry_depth + 1 local facedir = minetest.get_node(pos).param2 local owner = M(pos):get_string("owner") + local fake_player = techage.Fake_player:new() + fake_player.get_pos = function (...) + return pos + end + fake_player.get_inventory = function(...) + return M(pos):get_inventory() + end + + local add_to_inv = function(itemstacks) + local at_least_one_added = false + local inv = M(pos):get_inventory() + if #itemstacks == 0 then + return true + end + for _,stack in ipairs(itemstacks) do + if inv:room_for_item("main", stack) then + inv:add_item("main", stack) + at_least_one_added = true + elseif at_least_one_added then + minetest.add_item({x=pos.x,y=pos.y+1,z=pos.z}, stack) + end + end + return at_least_one_added + end local pos1, pos2 = get_corner_positions(pos, facedir, nvm.hole_diameter) nvm.level = 1 @@ -203,14 +210,13 @@ local function quarry_task(pos, crd, nvm) for zoffs = 1, nvm.hole_diameter do for xoffs = 1, nvm.hole_diameter do local qpos = get_quarry_pos(pos1, xoffs, zoffs) - local item_name = peek_node(qpos) - if item_name then - if add_to_inv(pos, item_name) then - minetest.remove_node(qpos) - crd.State:keep_running(pos, nvm, COUNTDOWN_TICKS) - else - crd.State:blocked(pos, nvm, S("inventory full")) - end + local dig_state = techage.dig_like_player(qpos, fake_player, add_to_inv) + + if dig_state == techage.dig_states.INV_FULL then + crd.State:blocked(pos, nvm, S("inventory full")) + coroutine.yield() + elseif dig_state == techage.dig_states.DUG then + crd.State:keep_running(pos, nvm, COUNTDOWN_TICKS) coroutine.yield() end end @@ -229,7 +235,10 @@ local function keep_running(pos, elapsed) local nvm = techage.get_nvm(pos) local crd = CRD(pos) - coroutine.resume(mem.co, pos, crd, nvm) + local _, err = coroutine.resume(mem.co, pos, crd, nvm) + if err then + minetest.log("error", "[TA4 Quarry Coroutine Error]" .. err) + end if techage.is_activeformspec(pos) then M(pos):set_string("formspec", formspec(crd.State, pos, nvm)) diff --git a/basis/fake_player.lua b/basis/fake_player.lua new file mode 100644 index 0000000..e4a6f26 --- /dev/null +++ b/basis/fake_player.lua @@ -0,0 +1,118 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019-2020 Joachim Stolberg + Copyright (C) 2020 Thomas S. + + GPL v3 + See LICENSE.txt for more information + + Fake Player + +]]-- + +-- Map method names to their return values +local methods = { + get_pos = { x = 0, y = 0, z = 0 }, + set_pos = nil, + moveto = nil, + punch = nil, + right_click = nil, + get_hp = 20, + set_hp = nil, + get_inventory = nil, + get_wield_list = "", + get_wield_index = 0, + get_wielded_item = ItemStack(), + set_wielded_item = true, + set_armor_groups = nil, + get_armor_groups = {}, + set_animation = nil, + get_animation = {}, + set_animation_frame_speed = nil, + set_attach = nil, + get_attach = nil, + set_detach = nil, + get_bone_position = {}, + set_properties = nil, + get_properties = {}, + is_player = false, + get_nametag_attributes = {}, + set_nametag_attributes = nil, + get_player_name = "", + get_player_velocity = nil, + add_player_velocity = nil, + get_look_dir = vector.new(0, 0, 1), + get_look_vertical = 0, + get_look_horizontal = 0, + set_look_vertical = nil, + set_look_horizontal = nil, + get_look_pitch = 0, + get_look_yaw = 0, + set_look_pitch = nil, + set_look_yaw = nil, + get_breath = 10, + set_breath = nil, + set_fov = nil, + get_fov = 0, + set_attribute = nil, + get_attribute = nil, + get_meta = nil, + set_inventory_formspec = nil, + get_inventory_formspec = "", + set_formspec_prepend = nil, + get_formspec_prepend = "", + get_player_control = {}, + get_player_control_bits = 0, + set_physics_override = nil, + get_physics_override = {}, + hud_add = 0, + hud_remove = nil, + hud_change = nil, + hud_get = {}, + hud_set_flags = nil, + hud_get_flags = {}, + hud_set_hotbar_itemcount = nil, + hud_get_hotbar_itemcount = 8, + hud_set_hotbar_image = nil, + hud_get_hotbar_image = "", + hud_set_hotbar_selected_image = nil, + hud_get_hotbar_selected_image = "", + set_sky = nil, + get_sky = {}, + get_sky_color = {}, + set_sun = nil, + get_sun = {}, + set_moon = nil, + get_moon = {}, + set_stars = nil, + get_stars = {}, + set_clouds = nil, + get_clouds = {}, + override_day_night_ratio = nil, + get_day_night_ratio = nil, + set_local_animation = nil, + get_local_animation = {}, + set_eye_offset = nil, + get_eye_offset = {}, + send_mapblock = nil, +} + +techage.Fake_player = {} +techage.Fake_player.__index = techage.Fake_player + +function techage.Fake_player:new() + local fake_player = {} + setmetatable(fake_player, techage.Fake_player) + return fake_player +end + + +for method_name, return_value in pairs(methods) do + techage.Fake_player[method_name] = function(self, ...) + return return_value + end +end + diff --git a/basis/lib.lua b/basis/lib.lua index 4f50747..9d954c4 100644 --- a/basis/lib.lua +++ b/basis/lib.lua @@ -140,7 +140,44 @@ function techage.can_node_dig(node, ndef) -- add it to the white list RegisteredNodesToBeDug[node.name] = true return true -end +end + +techage.dig_states = { + NOT_DIGGABLE = 1, + INV_FULL = 2, + DUG = 3 +} + +-- Digs a node like a player would by utilizing a fake player object. +-- add_to_inv(itemstacks) is a method that should try to add the dropped stacks to an appropriate inventory. +-- The node will only be dug, if add_to_inv(itemstacks) returns true. +function techage.dig_like_player(pos, fake_player, add_to_inv) + local node = techage.get_node_lvm(pos) + local ndef = minetest.registered_nodes[node.name] + if not ndef or ndef.diggable == false or (ndef.can_dig and not ndef.can_dig(pos, fake_player)) then + return techage.dig_states.NOT_DIGGABLE + end + local drop_as_strings = minetest.get_node_drops(node) + local drop_as_stacks = {} + for _,itemstring in ipairs(drop_as_strings) do + drop_as_stacks[#drop_as_stacks+1] = ItemStack(itemstring) + end + local meta = M(pos) + if ndef.preserve_metadata then + ndef.preserve_metadata(pos, node, meta, drop_as_stacks) + end + + if add_to_inv(drop_as_stacks) then + local oldmeta = meta:to_table() + minetest.remove_node(pos) + + if ndef.after_dig_node then + ndef.after_dig_node(pos, node, oldmeta, fake_player) + end + return techage.dig_states.DUG + end + return techage.dig_states.INV_FULL +end local function handle_drop(drop) -- To keep it simple, return only the item with the lowest rarity diff --git a/digtron/battery.lua b/digtron/battery.lua index c753081..4763f5e 100644 --- a/digtron/battery.lua +++ b/digtron/battery.lua @@ -141,7 +141,7 @@ techage.register_consumer("digtron_battery", S("Digtron Battery"), { act = tiles end end, preserve_metadata = function(pos, oldnode, oldmetadata, drops) - metadata = M(pos):to_table() + local metadata = M(pos):to_table() if metadata.inventory then local total = count_coal(metadata) local meta = drops[1]:get_meta() diff --git a/icta_controller/battery.lua b/icta_controller/battery.lua index fa59f1d..9f1a4f7 100644 --- a/icta_controller/battery.lua +++ b/icta_controller/battery.lua @@ -15,7 +15,6 @@ -- for lazy programmers local M = minetest.get_meta local S = techage.S -local logic = techage.logic local BATTERY_CAPACITY = 10000000 local function calc_percent(content) @@ -36,73 +35,69 @@ local function on_timer(pos, elapsed) return true end -local function register_battery(ext, percent, nici) - minetest.register_node("techage:ta4_battery"..ext, { - description = S("Battery").." "..ext, - inventory_image = 'techage_battery_inventory.png', - wield_image = 'techage_battery_inventory.png', - tiles = { - -- up, down, right, left, back, front - "techage_smartline.png", - "techage_smartline.png", - "techage_smartline.png", - "techage_smartline.png", - "techage_smartline.png", - "techage_smartline.png^techage_battery_green.png", - }, +minetest.register_alias("techage:ta4_battery75", "techage:ta4_battery") +minetest.register_alias("techage:ta4_battery50", "techage:ta4_battery") +minetest.register_alias("techage:ta4_battery25", "techage:ta4_battery") - drawtype = "nodebox", - node_box = { - type = "fixed", - fixed = { - { -6/32, -6/32, 14/32, 6/32, 6/32, 16/32}, - }, +minetest.register_node("techage:ta4_battery", { + description = S("Battery"), + inventory_image = 'techage_battery_inventory.png', + wield_image = 'techage_battery_inventory.png', + tiles = { + -- up, down, right, left, back, front + "techage_smartline.png", + "techage_smartline.png", + "techage_smartline.png", + "techage_smartline.png", + "techage_smartline.png", + "techage_smartline.png^techage_battery_green.png", + }, + + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { + { -6/32, -6/32, 14/32, 6/32, 6/32, 16/32}, }, - - after_place_node = function(pos, placer) - local meta = minetest.get_meta(pos) - meta:set_int("content", BATTERY_CAPACITY * percent) - local node = minetest.get_node(pos) - node.name = "techage:ta4_battery" - minetest.swap_node(pos, node) - on_timer(pos, 1) - minetest.get_node_timer(pos):start(30) - end, - - on_timer = on_timer, - - after_dig_node = function(pos, oldnode, oldmetadata, digger) - local percent = calc_percent(tonumber(oldmetadata.fields.content)) - local stack - if percent > 95 then - stack = ItemStack("techage:ta4_battery") - elseif percent > 75 then - stack = ItemStack("techage:ta4_battery75") - elseif percent > 50 then - stack = ItemStack("techage:ta4_battery50") - elseif percent > 25 then - stack = ItemStack("techage:ta4_battery25") - else - return + }, + + after_place_node = function(pos, placer, itemstack) + local content = BATTERY_CAPACITY + if itemstack then + local stack_meta = itemstack:get_meta() + if stack_meta then + -- This ensures that dug batteries of the old system are considered full. + local string_content = stack_meta:get_string("content") + if string_content ~= "" then + -- Batteries dug in the new system are handled correctly. + content = techage.in_range(stack_meta:get_int("content"), 0, BATTERY_CAPACITY) + end end - local inv = minetest.get_inventory({type="player", name=digger:get_player_name()}) - inv:add_item("main", stack) - end, + end + M(pos):set_int("content", content) + on_timer(pos, 1) + minetest.get_node_timer(pos):start(30) + end, - paramtype = "light", - sunlight_propagates = true, - paramtype2 = "facedir", - groups = {choppy=1, cracky=1, crumbly=1, not_in_creative_inventory=nici}, - drop = "", - is_ground_content = false, - sounds = default.node_sound_stone_defaults(), - }) -end + on_timer = on_timer, -register_battery("", 1.0, 0) -register_battery("75", 0.75, 1) -register_battery("50", 0.5, 1) -register_battery("25", 0.25, 1) + preserve_metadata = function(pos, oldnode, oldmetadata, drops) + local content = M(pos):get_int("content") + + local meta = drops[1]:get_meta() + meta:set_int("content", content) + local percent = calc_percent(content) + local text = S("Digtron Battery").." ("..percent.." %)" + meta:set_string("description", text) + end, + + paramtype = "light", + sunlight_propagates = true, + paramtype2 = "facedir", + groups = {choppy=1, cracky=1, crumbly=1, not_in_creative_inventory=nici}, + is_ground_content = false, + sounds = default.node_sound_stone_defaults(), +}) minetest.register_node("techage:ta4_battery_empty", { description = S("Battery"), @@ -159,8 +154,7 @@ else }) end -techage.register_node({"techage:ta4_battery", "techage:ta4_battery25", - "techage:ta4_battery50", "techage:ta4_battery75"}, +techage.register_node({"techage:ta4_battery"}, { on_node_load = function(pos) minetest.get_node_timer(pos):start(30) diff --git a/init.lua b/init.lua index 03351c3..6c94bd5 100644 --- a/init.lua +++ b/init.lua @@ -66,6 +66,7 @@ end -- Basis features local MP = minetest.get_modpath("techage") dofile(MP.."/basis/lib.lua") -- helper functions +dofile(MP.."/basis/fake_player.lua") -- dummy player object dofile(MP.."/basis/node_store.lua") dofile(MP.."/basis/gravel_lib.lua") -- ore probability dofile(MP.."/basis/node_states.lua") -- state model diff --git a/ta3_power/akkubox.lua b/ta3_power/akkubox.lua index 84dfcae..53a60e6 100644 --- a/ta3_power/akkubox.lua +++ b/ta3_power/akkubox.lua @@ -120,19 +120,15 @@ local function get_capa(itemstack) return 0 end -local function set_capa(pos, oldnode, digger, capa) - local node = ItemStack(oldnode.name) - local meta = node:get_meta() +local function set_capa(pos, oldnode, oldmetadata, drops) + local nvm = techage.get_nvm(pos) + local capa = nvm.capa + local meta = drops[1]:get_meta() capa = techage.power.percent(PWR_CAPA, capa) capa = (math.floor((capa or 0) / 5)) * 5 meta:set_int("capa", capa) local text = S("TA3 Accu Box").." ("..capa.." %)" meta:set_string("description", text) - local inv = minetest.get_inventory({type="player", name=digger:get_player_name()}) - local left_over = inv:add_item("main", node) - if left_over:get_count() > 0 then - minetest.add_item(pos, node) - end end local function after_place_node(pos, placer, itemstack) @@ -149,9 +145,7 @@ local function after_place_node(pos, placer, itemstack) end local function after_dig_node(pos, oldnode, oldmetadata, digger) - local nvm = techage.get_nvm(pos) Cable:after_dig_node(pos) - set_capa(pos, oldnode, digger, nvm.capa) techage.del_mem(pos) end @@ -188,13 +182,12 @@ minetest.register_node("techage:ta3_akku", { after_dig_node = after_dig_node, tubelib2_on_update2 = tubelib2_on_update2, networks = net_def, - - drop = "", -- don't remove, item will be added via 'set_capa' paramtype2 = "facedir", groups = {cracky=2, crumbly=2, choppy=2}, on_rotate = screwdriver.disallow, is_ground_content = false, sounds = default.node_sound_wood_defaults(), + preserve_metadata = set_capa, }) Cable:add_secondary_node_names({"techage:ta3_akku"})