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.
This commit is contained in:
Thomas--S 2020-09-12 09:12:45 +02:00
parent 21d73a307f
commit 0375cb1480
7 changed files with 257 additions and 105 deletions

View File

@ -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))

118
basis/fake_player.lua Normal file
View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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)

View File

@ -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

View File

@ -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"})