people/actions/building.lua

214 lines
8.9 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.building = function(state)
if type(state.action.dir) ~= "table" then
dbg.v1(state.ent.name.." has invalid dir for building action")
return true, false
end
if state.action.dir.y ~= 0 then
dbg.v1(state.ent.name.." has invalid y component of dir for building 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 building action")
return true, false
end
if type(state.action.height) ~= "number" or state.action.height < 1
or state.action.height > 5 then
dbg.v1(state.ent.name.." has invalid height for building action")
return true, false
end
if type(state.action.width) ~= "number" or state.action.width < 1
or state.action.width > 15 or state.action.width % 2 == 0 then
dbg.v1(state.ent.name.." has invalid width for building action")
return true, false
end
if type(state.action.depth) ~= "number" or state.action.depth < 1
or state.action.depth > 15 then
dbg.v1(state.ent.name.." has invalid depth for building action")
return true, false
end
if not state.action.startpos then
state.action.startpos = vector.round(state.pos)
end
if not state.action.cpos then
state.action.cpos = 0
end
-- The following only pans out if the width is an odd
-- number! TODO
local toleft = math.ceil(state.action.width / 2)
local toback = state.action.depth + 1
local toright = state.action.width + 1
local tofront = toback
local toleft2 = toleft
if state.action.atcpos then
-- So we're at a position, cpos...
-- 0 = in front of the entrance
-- 1 - (toleft - 1) = along front wall, including corner
if state.action.wall then
local wallpos
local starty = 0
if state.action.cpos > 0 and state.action.cpos < toleft then
wallpos = {x=0, z=1}
elseif state.action.cpos > toleft and state.action.cpos < toleft + toback - 1 then
-- Wall down left side of building...
wallpos = {x=1, z=0}
elseif state.action.cpos > toleft + toback and state.action.cpos < toleft + toback + toright then
-- Back wall
wallpos = {x=0, z=-1}
elseif state.action.cpos > toleft + toback + toright and state.action.cpos < toleft + toback + toright + tofront then
-- Wall down right side of building...
wallpos = {x=-1, z=0}
elseif state.action.cpos >= toleft + toback + toright + tofront + 1 and state.action.cpos < toleft + toback + toright + tofront + toleft2 then
wallpos = {x=0, z=1}
elseif state.action.cpos == toleft + toback + toright + tofront + toleft2 then
-- This should be the very last point on the circuit, which is
-- in front of the door!
wallpos = {x=0, z=1}
starty = 2
end
if wallpos then
for y = 0, state.action.height - 1 do
local thisoff = {x=wallpos.x, y=y, z=wallpos.z}
local woff = rotate(thisoff, state.action.dir)
local thispos = vector.add(state.pos, woff)
local dignode = minetest.get_node(thispos)
-- Note that we have to substring the dignode, because
-- the placed bottom half of the door doesn't have the
-- same name as the item it came from.
if y == 0 and starty == 2 and state.action.door and
dignode.name:sub(1, state.action.door:len())
~= state.action.door then
-- We place the door against the side, just to allow for
-- there being a right-click handling node (e.g.
-- junction) below it.
todir = {x=-1,y=0,z=0}
todir = rotate(todir, state.action.dir)
state.action = {"place", pos=thispos,
item=state.action.door,
successaction=state.action,
againstdir=todir}
return false
elseif y == starty and starty > 0 and dignode.name == "air" then
-- Similarly, we have to watch out if we're placing
-- above a door (it will open/shut!), or above a hole
-- where a door would go. Maybe the auto-against-dir
-- code in the place action should just be detecting
-- right-clickable things and avoiding them, to save
-- all this. Or 'holding shift'??
todir = {x=-1,y=0,z=0}
todir = rotate(todir, state.action.dir)
state.action = {"place", pos=thispos,
item=state.action.wall,
successaction=state.action,
againstdir=todir}
return false
elseif y >= starty and dignode.name == "air" then
state.action = {"place", pos=thispos, item=state.action.wall, successaction=state.action}
return false
elseif (y >= starty and dignode.name ~= state.action.wall)
or (y < starty and dignode.name ~= "air" and
dignode.name:sub(1, state.action.door:len())
~= state.action.door) then
state.action = {"dig", pos=thispos, successaction=state.action}
return false
end
end
end
end
state.action.atcpos = false
state.action.cpos = state.action.cpos + 1
if state.action.cpos > toleft + toback + toright + tofront + toleft2 then
dbg.v1(state.ent.name.." has completed outer circuit of walls")
return true, true
end
end
-- Calculate RELATIVE direction to move
local movedir
if state.action.cpos == 0 then
movedir = {x=0, z=-1}
elseif state.action.cpos <= toleft then
movedir = {x=-1, z=0}
elseif state.action.cpos <= toleft + toback then
movedir = {x=0, z=1}
elseif state.action.cpos <= toleft + toback + toright then
movedir = {x=1, z=0}
elseif state.action.cpos <= toleft + toback + toright + tofront then
movedir = {x=0, z=-1}
else
movedir = {x=-1, z=0}
end
movedir.y = 0
-- Rotate the movement direction to world orientation...
local wmovedir = rotate(movedir, state.action.dir)
local newdest = vector.add(vector.round(state.pos), wmovedir)
if state.action.outside_floor and state.action.cpos > 1 and
state.action.cpos < toleft + toback + toright + tofront + toleft2 then
local floor = state.action.outside_floor
local altfloor = nil
if type(floor) == "table" then
floor = state.action.outside_floor[1]
altfloor = state.action.outside_floor[2]
end
local thispos = {x=newdest.x, y=newdest.y - 1, z=newdest.z}
local dignode = minetest.get_node(thispos)
if dignode.name == "air" then
state.action = {"place", pos=thispos, item=floor, successaction=state.action}
return false
elseif dignode.name ~= floor and dignode.name ~= altfloor then
state.action = {"dig", pos=thispos, successaction=state.action}
return false
end
end
-- Make sure the area outside the walls is clear as we go round
for y = 0, state.action.height - 1 do
local thispos = {x=newdest.x, y=newdest.y + y, z=newdest.z}
local dignode = minetest.get_node(thispos)
if dignode.name ~= "air" then
state.action = {"dig", pos=thispos, successaction=state.action}
return false
end
end
dbg.v3(state.ent.name.." circling to "..minetest.pos_to_string(newdest)..
", cpos="..state.action.cpos..", movedir="..minetest.pos_to_string(movedir)..
", wmovedir="..minetest.pos_to_string(wmovedir))
state.action.atcpos = true
state.action = {"go", pos=newdest, successaction=state.action}
return false
end