plan generation finished, selection by dropdown

npc destination search optimizations for cleanup tasks
This commit is contained in:
Alexander Weber 2016-11-20 22:05:58 +01:00
parent b4f23d2b02
commit 1f12549e65
4 changed files with 209 additions and 98 deletions

View File

@ -16,14 +16,12 @@ local __get = function(pos)
local this = nil
if townchest.chest.list[key] then
this = townchest.chest.list[key]
dprint("get key from list", this)
else
this = townchest.chest.new()
this.key = key
this.pos = pos
this.meta = minetest.env:get_meta(pos) --just pointer
townchest.chest.list[key] = this
dprint("get new key", this)
end
-- update chest info
@ -114,9 +112,16 @@ townchest.chest.new = function()
-- Create the task that should be managed by chest
--------------------------------------
function chest.set_rawdata(this, taskname)
if taskname == "file" then
this.plan = townchest.plan.new(this)
local we = {}
if taskname then
this.info.taskname = taskname
end
if this.info.taskname == "file" then
-- check if file could be read
local we = townchest.files.readfile(this.info.filename)
we = townchest.files.readfile(this.info.filename)
if not we or #we == 0 then
this.infotext = "No building found in ".. this.info.filename
this:set_form("status")
@ -126,39 +131,59 @@ townchest.chest.new = function()
minetest.after(3, this.set_form, this, "file_open") --back to file selection
return
end
this.rawdata = we
elseif taskname == "generate" then
local we = {}
if this.info.genblock.fill == "true" then
--[[ for x = -math.floor(this.info.genblock.x/2), math.floor(this.info.genblock.x/2) do
for y = -1, this.info.genblock.y -2 do -- 1 under the chest
for z = -math.floor(this.info.genblock.z/2), math.floor(this.info.genblock.z/2) do
elseif this.info.taskname == "generate" then
if this.info.genblock.variant == 1 then
-- Fill with air
for x = 0, this.info.genblock.x-1 do
for y = 0, this.info.genblock.y-1 do
for z = 0, this.info.genblock.z-1 do
table.insert(we, {x=x,y=y,z=z, name = "air"})
end
end
end
elseif this.info.genblock.variant == 2 then
-- Fill with stone
for x = 0, this.info.genblock.x-1 do
for y = 0, this.info.genblock.y-1 do
for z = 0, this.info.genblock.z-1 do
table.insert(we, {x=x,y=y,z=z, name = "default:cobble"})
end
end
end
]]
for x = -math.floor(this.info.genblock.x/2), math.floor(this.info.genblock.x/2) do
for y = -1, this.info.genblock.y -2 do -- 1 under the chest
for z = -math.floor(this.info.genblock.z/2), math.floor(this.info.genblock.z/2) do
if x == -math.floor(this.info.genblock.x/2) or x == math.floor(this.info.genblock.x/2) or
y == -1 or y == this.info.genblock.y -2 or
z == -math.floor(this.info.genblock.z/2) or z == math.floor(this.info.genblock.z/2) then
elseif this.info.genblock.variant == 3 then
-- Build a box
for x = 0, this.info.genblock.x-1 do
for y = 0, this.info.genblock.y-1 do
for z = 0, this.info.genblock.z-1 do
if x == 0 or x == this.info.genblock.x-1 or
y == 0 or y == this.info.genblock.y-1 or
z == 0 or z == this.info.genblock.z-1 then
table.insert(we, {x=x,y=y,z=z, name = "default:cobble"})
end
end
end
end
else
table.insert(we, {x=-math.floor(this.info.genblock.x/2),y=0,z=-math.floor(this.info.genblock.z/2), name = "air"})
table.insert(we, {x=math.floor(this.info.genblock.x/2),y=this.info.genblock.y -1,z=math.floor(this.info.genblock.z/2), name = "air"})
-- build ground level under chest
this.plan.relative.ground_y = 1
-- Build a plate
elseif this.info.genblock.variant == 4 then
local y = 0
for x = 0, this.info.genblock.x-1 do
for z = 0, this.info.genblock.z-1 do
table.insert(we, {x=x,y=y,z=z, name = "default:cobble"})
end
end
-- build ground level under chest
this.plan.relative.ground_y = 1
end
this.rawdata = we
end
this.info.taskname = taskname
this.plan = townchest.plan.new(this)
this.rawdata = we
chest:run_async(this.prepare_building_plan_chain)
end
@ -300,7 +325,7 @@ townchest.chest.new = function()
dprint("restoral info", dump(chestinfo))
if chestinfo.taskname and not this.current_stage then -- file selected but no plan. Restore the plan
this.current_stage = "restore"
chest:set_rawdata(this.info.taskname)
chest:set_rawdata(chestinfo.taskname)
elseif not chestinfo.filename then
this:set_form("file_open")
end

View File

@ -1,6 +1,7 @@
local dprint = townchest.dprint --debug
local MAX_SPEED = 5
local BUILD_DISTANCE = 3
townchest.npc = {
@ -54,8 +55,6 @@ local select_chest = function(self)
self.chest = nil
self.chestpos = nil
end
else
dprint("Chest ok:",self.chest)
end
end
@ -122,12 +121,12 @@ local function prefer_target(npc, t1, t2)
end
local npcpos = npc.object:getpos()
-- npc is 1.5 blocks over the work
npcpos.y = npcpos.y - 1.5
-- variables for vectors based preference manipulation
-- variables for preference manipulation
local t1_c = {x=t1.pos.x, y=t1.pos.y, z=t1.pos.z}
local t2_c = {x=t2.pos.x, y=t2.pos.y, z=t2.pos.z}
-- variable for value based preference manipulation
local prefer = 0
--prefer same items in building order
@ -140,37 +139,56 @@ local function prefer_target(npc, t1, t2)
end
end
-- note: npc is higher by y+1.5
-- in case of clanup task prefer higher node
-- prefer the last target node
if npc.targetnode then
if t1.x == npc.targetnode.pos.x and
t1.y == npc.targetnode.pos.y and
t1.z == npc.targetnode.pos.z then
prefer = prefer + BUILD_DISTANCE
end
if t2.x == npc.targetnode.pos.x and
t2.y == npc.targetnode.pos.y and
t2.z == npc.targetnode.pos.z then
prefer = prefer - BUILD_DISTANCE
end
end
-- prefer air in general
if t1.name == "air" then
prefer = prefer + 2
end
if t2.name == "air" then
prefer = prefer - 2
end
-- prefer lower node if not air
if t1.name ~= "air" then
-- calculate as over the npc by additional 1.5. no change means lower then npc by 1.5
t1_c.y = t1_c.y + 3
else
-- prefer air
t1_c.y = t1_c.y - 2
t1_c.y = t1_c.y + 1
elseif math.abs(npcpos.y - t1.pos.y) <= BUILD_DISTANCE then
-- prefer higher node if air in reachable distance
t1_c.y = t1_c.y - 4
end
-- prefer lower node if not air
if t2.name ~= "air" then
-- calculate as over the npc by additional 1.5. no change means lower then npc by 1.5
t2_c.y = t2_c.y + 3
else
-- prefer air
t2_c.y = t2_c.y - 2
t2_c.y = t2_c.y + 1
elseif math.abs(npcpos.y - t1.pos.y) <= BUILD_DISTANCE then
-- prefer higher node if air in reachable distance
t2_c.y = t2_c.y - 4
end
-- avoid build directly under or over the npc. No extra bonus for air in this case
if math.abs(npcpos.x - t1.pos.x) < 1 and math.abs(npcpos.z - t1.pos.z) < 1 then
prefer = prefer-2
elseif t1.name == "air" then
prefer = prefer+2
-- avoid build directly under or in the npc
if math.abs(npcpos.x - t1.pos.x) < 0.5 and
math.abs(npcpos.y - t1.pos.y) < 2 and
math.abs(npcpos.z - t1.pos.z) < 0.5 then
prefer = prefer-1.5
end
if math.abs(npcpos.x - t2.pos.x) < 1 and math.abs(npcpos.z - t2.pos.z) < 1 then
prefer = prefer+2
elseif t2.name == "air" then
prefer = prefer-2
if math.abs(npcpos.x - t2.pos.x) < 0.5 and
math.abs(npcpos.y - t1.pos.y) < 2 and
math.abs(npcpos.z - t2.pos.z) < 0.5 then
prefer = prefer+1.5
end
-- compare
if vector.distance(npcpos, t1_c) - prefer > vector.distance(npcpos, t2_c) then
return t2
@ -303,7 +321,7 @@ npcf:register_npc("townchest:npcf_builder" ,{
yaw = npcf:get_face_direction(pos, self.targetnode.pos)
-- target reached build
if target_distance < 3 and self.dest_type == "build" then
if target_distance <= BUILD_DISTANCE and self.dest_type == "build" then
-- do the build
local soundspec
if minetest.registered_items[self.targetnode.name].sounds then
@ -332,13 +350,6 @@ npcf:register_npc("townchest:npcf_builder" ,{
if target_distance > 2 then
speed = 1
state = NPCF_ANIM_WALK_MINE
-- jump
if self.targetnode.name ~= "air" and (self.targetnode.pos.y -(pos.y-1.5)) > 0 and (self.targetnode.pos.y -(pos.y-1.5)) < 2 then
acceleration = {x=0, y=0, z=0}
pos = {x=pos.x, y=self.targetnode.pos.y + 1.5, z=pos.z}
self.object:setpos(pos)
end
else
speed = 0
state = NPCF_ANIM_MINE
@ -359,14 +370,15 @@ npcf:register_npc("townchest:npcf_builder" ,{
--target not reached
state = NPCF_ANIM_WALK
-- Big jump / teleport upsite
if (self.targetnode.pos.y -(pos.y-1.5)) > 0 and
math.abs(self.targetnode.pos.x - pos.x) <= 3 and
math.abs(self.targetnode.pos.z - pos.z) <= 3 then
if (self.targetnode.pos.y -(pos.y-1.5)) > BUILD_DISTANCE and
math.abs(self.targetnode.pos.x - pos.x) <= 0.5 and
math.abs(self.targetnode.pos.z - pos.z) <= 0.5 then
acceleration = {x=0, y=0, z=0}
pos = {x=pos.x, y=self.targetnode.pos.y + 1.5, z=pos.z}
self.object:setpos(pos)
target_distance = 0 -- to skip the next part and set speed to 0
local state = NPCF_ANIM_STAND
state = NPCF_ANIM_STAND
dprint("Big jump to"..minetest.pos_to_string(pos))
end
-- teleport in direction in case of stucking
@ -379,6 +391,9 @@ npcf:register_npc("townchest:npcf_builder" ,{
end
self.object:setpos(pos)
acceleration = {x=0, y=0, z=0}
target_distance = 0 -- to skip the next part and set speed to 0
state = NPCF_ANIM_STAND
dprint("Teleport to"..minetest.pos_to_string(pos))
end
self.var.last_pos = pos
speed = get_speed(target_distance)

View File

@ -1,16 +1,15 @@
local dprint = townchest.dprint_off --debug
townchest.specwidgets = {}
--- temporary provide smartfs as builtin, till the needed changes are upstream
local smartfs = townchest.smartfs
--- temporary end
-----------------------------------------------
-- file open dialog form / (tabbed)
-----------------------------------------------
local _file_open_dialog = function(state)
--connect to chest object for data
local chest = townchest.chest.get(state.location.pos)
@ -42,7 +41,9 @@ local _file_open_dialog = function(state)
end,
}
-- file selection tab button
-----------------------------------------------
-- file selection tab
-----------------------------------------------
local tab1 = {}
tab1.button = state:button(0,0,2,1,"tab1_btn","Buildings")
tab1.button:onClick(function(self)
@ -58,20 +59,29 @@ local _file_open_dialog = function(state)
end
tab_controller:tab_add("tab1", tab1)
-----------------------------------------------
-- Simple form building tab
-----------------------------------------------
-- Tasks tab button
local tab2 = {}
tab2.button = state:button(2,0,2,1,"tab2_btn","Tasks")
tab2.button:onClick(function(self)
tab_controller:set_active("tab2")
end)
tab2.view = state:view(0.5,1,"tab2_view")
tab2.view = state:view(0,1,"tab2_view")
tab2.viewstate = tab2.view:getViewState()
-- Tasks tab view state
tab2.viewstate:label(0,0,"header","Free place for a build")
tab2.viewstate:label(0,0.2,"header","Build simple form")
local variant = tab2.viewstate:dropdown(3.5,0.2,4,0.5,"variant", 1)
variant:addItem("Fill with air") -- 1
variant:addItem("Fill with stone") -- 2
variant:addItem("Build a box") -- 3
variant:addItem("Build a plate") -- 4
local field_x = tab2.viewstate:field(0,2,2,0.5,"x","width (x)")
local field_y = tab2.viewstate:field(2,2,2,0.5,"y","high (y)")
local field_z = tab2.viewstate:field(4,2,2,0.5,"z","width (y)")
local fill_chk = tab2.viewstate:checkbox(0,3,"fill", "Fill place with stone")
tab_controller:tab_add("tab2", tab2)
--process all inputs
@ -79,7 +89,8 @@ local _file_open_dialog = function(state)
chest.info.genblock.x = tonumber(field_x:getText())
chest.info.genblock.y = tonumber(field_y:getText())
chest.info.genblock.z = tonumber(field_z:getText())
chest.info.genblock.fill = fill_chk:getValue()
chest.info.genblock.variant = variant:getSelected()
chest.info.genblock.variant_name = variant:getSelectedItem()
chest:persist_info()
end)
@ -108,9 +119,10 @@ local _file_open_dialog = function(state)
field_x:setText(tostring(chest.info.genblock.x or 1))
field_y:setText(tostring(chest.info.genblock.y or 1))
field_z:setText(tostring(chest.info.genblock.z or 1))
fill_chk:setValue(chest.info.genblock.fill)
variant:setSelected(chest.info.genblock.variant or 1)
return true --successfull build, update needed
--successfull build, update needed
return true
end
smartfs.create("file_open", _file_open_dialog)
@ -165,7 +177,7 @@ local _build_status = function(state)
if chest.info.taskname == "file" then
l1:setText("Building "..chest.info.filename.." selected")
elseif chest.info.taskname == "generate" then
l1:setText("Simple task")
l1:setText("Simple task: "..chest.info.genblock.variant_name)
end
l2:setText("Size: "..(relative.max_x-relative.min_x).." x "..(relative.max_z-relative.min_z))
l3:setText("Building high: "..(relative.max_y-relative.min_y).." Ground high: "..(relative.ground_y-relative.min_y))

View File

@ -742,6 +742,14 @@ function smartfs._makeState_(form, newplayer, params, is_inv, nodepos)
transparent = transparent
})
end,
dropdown = function(self, x, y, w, h, name, selected)
return self:element("dropdown", {
pos = {x=x, y=y},
size = {w=w, h=h},
name = name,
selected = selected
})
end,
inventory = function(self, x, y, w, h, name)
return self:element("inventory", {
pos = {x=x, y=y},
@ -1002,7 +1010,6 @@ smartfs.element("list", {
assert(self.data.size and self.data.size.w and self.data.size.h, "list needs valid size")
assert(self.name, "list needs name")
self.data.value = minetest.is_yes(self.data.value)
self.data.label = self.data.label or ""
self.data.items = self.data.items or {}
end,
build = function(self)
@ -1041,27 +1048,17 @@ smartfs.element("list", {
self._doubleClick = func
end,
addItem = function(self, item)
if not self.data.items then
self.data.items = {}
end
table.insert(self.data.items, item)
-- return the index of item. It is the last one
return #self.data.items
end,
removeItem = function(self,idx)
if not self.data.items then
self.data.items = {}
end
table.remove(self.data.items,idx)
end,
getItem = function(self, idx)
if not self.data.items then
self.data.items = {}
end
return self.data.items[idx]
end,
popItem = function(self)
if not self.data.items then
self.data.items = {}
end
local item = self.data.items[#self.data.items]
table.remove(self.data.items)
return item
@ -1080,6 +1077,75 @@ smartfs.element("list", {
end,
})
smartfs.element("dropdown", {
onCreate = function(self)
assert(self.data.pos and self.data.pos.x and self.data.pos.y, "dropdown needs valid pos")
assert(self.data.size and self.data.size.w and self.data.size.h, "dropdown needs valid size")
assert(self.name, "dropdown needs name")
self.data.items = self.data.items or {}
self.data.selected = self.data.selected or 1
self.data.value = ""
end,
build = function(self)
return "dropdown["..
self:getPosString()..";"..
self:getSizeString()..";"..
self:getAbsName()..";"..
table.concat(self.data.items, ",")..";"..
tostring(self:getSelected()).."]"..
self:getBackgroundString()
end,
submit = function(self, field, player)
self:getSelected()
if self._select then
self:_select(self.root, field, player)
end
end,
onSelect = function(self, func)
self._select = func
end,
addItem = function(self, item)
table.insert(self.data.items, item)
if #self.data.items == self.data.selected then
self.data.value = item
end
-- return the index of item. It is the last one
return #self.data.items
end,
removeItem = function(self,idx)
table.remove(self.data.items,idx)
end,
getItem = function(self, idx)
return self.data.items[idx]
end,
popItem = function(self)
local item = self.data.items[#self.data.items]
table.remove(self.data.items)
return item
end,
clearItems = function(self)
self.data.items = {}
end,
setSelected = function(self,idx)
self.data.selected = idx
self.data.value = self:getItem(idx) or ""
end,
getSelected = function(self)
self.data.selected = 1
if #self.data.items > 1 then
for i = 1, #self.data.items do
if self.data.items[i] == self.data.value then
self.data.selected = i
end
end
end
return self.data.selected
end,
getSelectedItem = function(self)
return self.data.value
end,
})
smartfs.element("inventory", {
onCreate = function(self)
assert(self.data.pos and self.data.pos.x and self.data.pos.y, "list needs valid pos")
@ -1157,6 +1223,7 @@ smartfs.element("view", {
onCreate = function(self)
assert(self.data.pos and self.data.pos.x and self.data.pos.y, "view needs valid pos")
assert(self.name, "view needs name")
self._state = smartfs._makeState_(self, nil, self.root.param)
end,
-- redefinitions. The size is not handled by data.size but by view-state:size
setSize = function(self,w,h)
@ -1167,17 +1234,9 @@ smartfs.element("view", {
end,
-- element interface methods
build = function(self)
if not self:getIsHiddenOrCutted() == true then
return self:getViewState():_buildFormspec_(false)..self:getBackgroundString()
else
print("SmartFS - (Warning): view outside or hidden")
return ""
end
return self:getViewState():_buildFormspec_(false)..self:getBackgroundString()
end,
getViewState = function(self)
if not self._state then
self._state = smartfs._makeState_(self, nil, self.root.param)
end
return self._state
end
-- submit is handled by framework for elements with getViewState