people/actions/tunnel.lua

269 lines
9.7 KiB
Lua

local dbg
if moddebug then dbg=moddebug.dbg("people") else dbg={v1=function() end,v2=function() end,v3=function() end} end
local function rotate(rel, dir)
if dir.z < 0 then
return {x=-rel.x, y=rel.y, z=-rel.z}
elseif dir.z > 0 then
return {x=rel.x, y=rel.y, z=rel.z}
elseif dir.x > 0 then
return {x=rel.z, y=rel.y, z=-rel.x}
else
return {x=-rel.z, y=rel.y, z=rel.x}
end
end
people.actions.tunnel = function(state)
if type(state.action.distance) ~= "number" then
dbg.v1(state.ent.name.." has invalid distance for tunnel action")
return true, false
end
if type(state.action.dir) ~= "table" then
dbg.v1(state.ent.name.." has invalid dir for tunnel action")
return true, false
end
if state.action.dir.y ~= 0 then
dbg.v1(state.ent.name.." has invalid y component of dir for tunnel action")
return true, false
end
if math.abs(state.action.dir.x) + math.abs(state.action.dir.z) ~= 1 then
dbg.v1(state.ent.name.." has invalid x/z components of dir for tunnel action")
return true, false
end
-- On starting, keep the original position and distance. This will allow
-- us to break off and resume if, for example, we run out of materials or
-- room to carry the spoils.
if not state.action.startpos then
state.action.startpos = vector.round(state.pos)
state.action.startdist = state.action.distance
end
if(state.action.distance <= 0) then
dbg.v1(state.ent.name.." completed tunnelling")
return true, true
end
-- digpos will be the base centre of the new cross-sectional area
-- we're going to dig.
local digpos = vector.add(vector.round(state.pos), state.action.dir)
-- Tweak (hack) because if we're laying steps, we're standing on them as
-- we go down. This sucks, and even more so because we might want to
-- skip the steps until another time if we don't have the materials
if state.action.dir.y == -1 and state.action.steps and state.action.distance ~= state.action.startdist then
digpos.y = digpos.y - 1
end
local relxaxis
if state.action.dir.x == 0 then
relxaxis = "x"
else
relxaxis = "z"
end
local height = 2
if state.action.dir.y ~= 0 then
if state.action.steps then
height = 4
else
height = 3
end
end
-- Make it so we dig from top to bottom when tunnelling downwards, and
-- bottom to top otherwise.
local yfrom, yto, ydir
if state.action.dir.y < 0 then
yfrom = height
yto = 0
ydir = -1
else
yfrom = 0
yto = height
ydir = 1
end
-- "Special" positions are those where supports and lighting are placed
local special_sup = (state.action.startdist - state.action.distance + 2) % 8 == 7
local special_light = (state.action.startdist - state.action.distance) % 4 == 3
-- TODO - the following is all a bit ineffecient, because we look at the
-- whole cross-section every time through. We could keep track of
-- where we've got to during a run and skip some.
for y = yfrom, yto, ydir do
for x = -1, 1 do
local thisoff = {x=0, y=y, z=0}
thisoff[relxaxis] = x
local thispos = vector.add(digpos, thisoff)
local dignode = minetest.get_node(thispos)
-- Steps at the bottom row if needed.
if y == 0 and state.action.steps and state.action.dir.y ~= 0 then
if dignode.name == "air" then
state.action = {"place", pos=thispos, item=state.action.steps, successaction=state.action,
param2=minetest.dir_to_facedir({x=-state.action.dir.x,y=0,z=-state.action.dir.z}, false)}
return false
elseif dignode.name ~= state.action.steps then
state.action = {"dig", pos=thispos, successaction=state.action}
return false
end
elseif y == 2 and x ~= 0 and state.action.lighting and special_light then
-- We don't actually place lighting (e.g. torches) here,
-- because we do walls later and that would disturb them.
-- But we need to make sure we don't remove existing ones.
if dignode.name ~= "air" and
dignode.name ~= state.action.lighting then
state.action = {"dig", pos=thispos, successaction=state.action}
return false
end
elseif y == height and state.action.support_top and special_sup then
if dignode.name == "air" then
state.action = {"place", pos=thispos, item=state.action.support_top, successaction=state.action}
return false
elseif dignode.name ~= state.action.support_top then
state.action = {"dig", pos=thispos, successaction=state.action}
return false
end
elseif x ~= 0 and state.action.support_side and special_sup then
if dignode.name == "air" then
local param2
if state.action.support_side_facein then
local facevec = rotate({x=-x, y=0, z=0},
state.action.dir)
if facevec.x > 0 then
param2 = 4 * 4
elseif facevec.x < 0 then
param2 = 4 * 3
elseif facevec.z > 0 then
param2 = 4 * 2
else
param2 = 4 * 1
end
end
state.action = {"place", pos=thispos,
item=state.action.support_side,
successaction=state.action,
param2=param2}
return false
elseif dignode.name ~= state.action.support_side then
state.action = {"dig", pos=thispos, successaction=state.action}
return false
end
else
if dignode.name ~= "air" then
state.action = {"dig", pos=thispos, successaction=state.action}
return false
end
end
end
end
if state.action.floor and state.action.dir.y == 0 then
for x = -1, 1 do
local thisoff = {x=0, y=-1, z=0}
thisoff[relxaxis] = x
local thispos = vector.add(digpos, thisoff)
local dignode = minetest.get_node(thispos)
if dignode.name == "air" then
state.action = {"place", pos=thispos, item=state.action.floor, successaction=state.action}
return false
elseif dignode.name ~= state.action.floor then
state.action = {"dig", pos=thispos, successaction=state.action}
return false
end
end
end
local wall = state.action.wall
if special_sup and state.action.support_wall then
wall = state.action.support_wall
end
if wall then
for y = 0, height + 1 do
local thisoff = {x=0, y=y, z=0}
for x = -2, 2, 4 do
thisoff[relxaxis] = x
local thispos = vector.add(digpos, thisoff)
local dignode = minetest.get_node(thispos)
if dignode.name == "air" then
state.action = {"place", pos=thispos, item=wall, successaction=state.action}
return false
elseif dignode.name ~= wall then
state.action = {"dig", pos=thispos, successaction=state.action}
return false
end
end
end
end
if state.action.lighting and special_light then
local thisoff = {x=0, y=2, z=0}
for x = -1, 1, 2 do
thisoff[relxaxis] = x
local thispos = vector.add(digpos, thisoff)
local dignode = minetest.get_node(thispos)
if dignode.name ~= state.action.lighting then
local againstdir = {x=0, y=0, z=0}
againstdir[relxaxis] = x
state.action = {"place", pos=thispos, item=state.action.lighting, successaction=state.action,
againstdir=againstdir}
return false
end
end
end
local ceiling = state.action.ceiling
if special_sup and state.action.support_ceiling then
ceiling = state.action.support_ceiling
end
if ceiling then
-- We need to place the ceiling nodes against something - we use
-- the walls.
local againstdir = {x=0, y=0, z=0}
againstdir[relxaxis] = -1
for x = -1, 1 do
local thisoff = {x=0, y=height+1, z=0}
thisoff[relxaxis] = x
local thispos = vector.add(digpos, thisoff)
local dignode = minetest.get_node(thispos)
if dignode.name == "air" then
state.action = {"place", pos=thispos, item=ceiling, successaction=state.action,
againstdir=againstdir}
return false
elseif dignode.name ~= ceiling then
state.action = {"dig", pos=thispos, successaction=state.action}
return false
end
end
end
-- We've cleared the cross-section at the next position, so we can move
-- forward a step...
state.action.distance = state.action.distance - 1
state.action = {"go", pos=digpos, successaction=state.action}
return false
end