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

View File

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

View File

@ -1,16 +1,15 @@
local dprint = townchest.dprint_off --debug local dprint = townchest.dprint_off --debug
townchest.specwidgets = {} townchest.specwidgets = {}
--- temporary provide smartfs as builtin, till the needed changes are upstream --- temporary provide smartfs as builtin, till the needed changes are upstream
local smartfs = townchest.smartfs local smartfs = townchest.smartfs
--- temporary end --- temporary end
-----------------------------------------------
-- file open dialog form / (tabbed)
-----------------------------------------------
local _file_open_dialog = function(state) local _file_open_dialog = function(state)
--connect to chest object for data --connect to chest object for data
local chest = townchest.chest.get(state.location.pos) local chest = townchest.chest.get(state.location.pos)
@ -42,7 +41,9 @@ local _file_open_dialog = function(state)
end, end,
} }
-- file selection tab button -----------------------------------------------
-- file selection tab
-----------------------------------------------
local tab1 = {} local tab1 = {}
tab1.button = state:button(0,0,2,1,"tab1_btn","Buildings") tab1.button = state:button(0,0,2,1,"tab1_btn","Buildings")
tab1.button:onClick(function(self) tab1.button:onClick(function(self)
@ -58,20 +59,29 @@ local _file_open_dialog = function(state)
end end
tab_controller:tab_add("tab1", tab1) tab_controller:tab_add("tab1", tab1)
-----------------------------------------------
-- Simple form building tab
-----------------------------------------------
-- Tasks tab button -- Tasks tab button
local tab2 = {} local tab2 = {}
tab2.button = state:button(2,0,2,1,"tab2_btn","Tasks") tab2.button = state:button(2,0,2,1,"tab2_btn","Tasks")
tab2.button:onClick(function(self) tab2.button:onClick(function(self)
tab_controller:set_active("tab2") tab_controller:set_active("tab2")
end) end)
tab2.view = state:view(0.5,1,"tab2_view") tab2.view = state:view(0,1,"tab2_view")
tab2.viewstate = tab2.view:getViewState() tab2.viewstate = tab2.view:getViewState()
-- Tasks tab view state -- 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_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_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 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) tab_controller:tab_add("tab2", tab2)
--process all inputs --process all inputs
@ -79,7 +89,8 @@ local _file_open_dialog = function(state)
chest.info.genblock.x = tonumber(field_x:getText()) chest.info.genblock.x = tonumber(field_x:getText())
chest.info.genblock.y = tonumber(field_y:getText()) chest.info.genblock.y = tonumber(field_y:getText())
chest.info.genblock.z = tonumber(field_z: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() chest:persist_info()
end) end)
@ -108,9 +119,10 @@ local _file_open_dialog = function(state)
field_x:setText(tostring(chest.info.genblock.x or 1)) field_x:setText(tostring(chest.info.genblock.x or 1))
field_y:setText(tostring(chest.info.genblock.y or 1)) field_y:setText(tostring(chest.info.genblock.y or 1))
field_z:setText(tostring(chest.info.genblock.z 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 end
smartfs.create("file_open", _file_open_dialog) smartfs.create("file_open", _file_open_dialog)
@ -165,7 +177,7 @@ local _build_status = function(state)
if chest.info.taskname == "file" then if chest.info.taskname == "file" then
l1:setText("Building "..chest.info.filename.." selected") l1:setText("Building "..chest.info.filename.." selected")
elseif chest.info.taskname == "generate" then elseif chest.info.taskname == "generate" then
l1:setText("Simple task") l1:setText("Simple task: "..chest.info.genblock.variant_name)
end end
l2:setText("Size: "..(relative.max_x-relative.min_x).." x "..(relative.max_z-relative.min_z)) 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)) 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 transparent = transparent
}) })
end, 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) inventory = function(self, x, y, w, h, name)
return self:element("inventory", { return self:element("inventory", {
pos = {x=x, y=y}, 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.data.size and self.data.size.w and self.data.size.h, "list needs valid size")
assert(self.name, "list needs name") assert(self.name, "list needs name")
self.data.value = minetest.is_yes(self.data.value) self.data.value = minetest.is_yes(self.data.value)
self.data.label = self.data.label or ""
self.data.items = self.data.items or {} self.data.items = self.data.items or {}
end, end,
build = function(self) build = function(self)
@ -1041,27 +1048,17 @@ smartfs.element("list", {
self._doubleClick = func self._doubleClick = func
end, end,
addItem = function(self, item) addItem = function(self, item)
if not self.data.items then
self.data.items = {}
end
table.insert(self.data.items, item) table.insert(self.data.items, item)
-- return the index of item. It is the last one
return #self.data.items
end, end,
removeItem = function(self,idx) removeItem = function(self,idx)
if not self.data.items then
self.data.items = {}
end
table.remove(self.data.items,idx) table.remove(self.data.items,idx)
end, end,
getItem = function(self, idx) getItem = function(self, idx)
if not self.data.items then
self.data.items = {}
end
return self.data.items[idx] return self.data.items[idx]
end, end,
popItem = function(self) popItem = function(self)
if not self.data.items then
self.data.items = {}
end
local item = self.data.items[#self.data.items] local item = self.data.items[#self.data.items]
table.remove(self.data.items) table.remove(self.data.items)
return item return item
@ -1080,6 +1077,75 @@ smartfs.element("list", {
end, 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", { smartfs.element("inventory", {
onCreate = function(self) onCreate = function(self)
assert(self.data.pos and self.data.pos.x and self.data.pos.y, "list needs valid pos") 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) onCreate = function(self)
assert(self.data.pos and self.data.pos.x and self.data.pos.y, "view needs valid pos") assert(self.data.pos and self.data.pos.x and self.data.pos.y, "view needs valid pos")
assert(self.name, "view needs name") assert(self.name, "view needs name")
self._state = smartfs._makeState_(self, nil, self.root.param)
end, end,
-- redefinitions. The size is not handled by data.size but by view-state:size -- redefinitions. The size is not handled by data.size but by view-state:size
setSize = function(self,w,h) setSize = function(self,w,h)
@ -1167,17 +1234,9 @@ smartfs.element("view", {
end, end,
-- element interface methods -- element interface methods
build = function(self) build = function(self)
if not self:getIsHiddenOrCutted() == true then return self:getViewState():_buildFormspec_(false)..self:getBackgroundString()
return self:getViewState():_buildFormspec_(false)..self:getBackgroundString()
else
print("SmartFS - (Warning): view outside or hidden")
return ""
end
end, end,
getViewState = function(self) getViewState = function(self)
if not self._state then
self._state = smartfs._makeState_(self, nil, self.root.param)
end
return self._state return self._state
end end
-- submit is handled by framework for elements with getViewState -- submit is handled by framework for elements with getViewState