Switch to using footpath mod
parent
cb29cd13d7
commit
642cc58348
|
@ -30,7 +30,9 @@ the default minetest game.
|
|||
## Requirements
|
||||
|
||||
Optionally, you might want these mods:
|
||||
* areas (my fork of it) - https://github.com/CiaranG/areas - for footpath
|
||||
* areas (my fork of it) - https://github.com/CiaranG/areas - for being able to
|
||||
specify an area as a destination
|
||||
* footpath - https://gitlab.com/CiaranG/moddebug - for full route finding and
|
||||
navigation support
|
||||
* moddebug - https://gitlab.com/CiaranG/moddebug - for debug logging
|
||||
|
||||
|
|
119
actions/go.lua
119
actions/go.lua
|
@ -17,61 +17,60 @@ people.actions.go = function(state)
|
|||
if (not state.action.pos) and (not state.action.footpathdest) and state.action.name then
|
||||
|
||||
-- The action says to go to a named place. We're going to convert this
|
||||
-- into a position, ready for the next call...
|
||||
|
||||
if not areas then
|
||||
dbg.v1(state.ent.name.." can't go to named destination without areas mod")
|
||||
return true, false
|
||||
end
|
||||
-- into a position (if we have a way of doing so, e.g. via the footpath
|
||||
-- or areas mods), ready for the next call...
|
||||
|
||||
if type(state.action.name) ~= "string" then
|
||||
dbg.v1(state.ent.name.." can't go to a named destination that isn't a string")
|
||||
return true, false
|
||||
end
|
||||
|
||||
-- Look for a footpath node with the right name
|
||||
local pathdestarea = areas:findNearestArea(state.pos, "^path_"..people.escape_pattern(state.action.name).."$")
|
||||
if pathdestarea then
|
||||
-- Look for a footpath node with the right name
|
||||
if footpath then
|
||||
local pathdestpos = footpath.get_nodepos("path_"..state.action.name)
|
||||
if pathdestpos then
|
||||
|
||||
dbg.v2(state.ent.name.." selected path destination "..pathdestarea.name)
|
||||
dbg.v2(state.ent.name.." selected path destination path_"..state.action.name)
|
||||
|
||||
-- Find the nearest footpath junction to us. We'll head to that
|
||||
-- first (as a pos) and then follow the footpath network from
|
||||
-- there.
|
||||
local pathstartarea = areas:findNearestArea(state.pos, "^path_.+")
|
||||
if pathstartarea then
|
||||
dbg.v2(state.ent.name.." selected path start "..pathstartarea.name.." at "..minetest.pos_to_string(pathstartarea.pos1))
|
||||
state.action.pos = vector.new(pathstartarea.pos1)
|
||||
state.action.pos.y = state.action.pos.y + ANTP
|
||||
state.action.footpathdest = pathdestarea
|
||||
return false
|
||||
-- Find the nearest footpath junction to us. We'll head to that
|
||||
-- first (as a pos) and then follow the footpath network from
|
||||
-- there.
|
||||
local pathstartnode = footpath.nearest_node(state.pos)
|
||||
if pathstartnode then
|
||||
dbg.v2(state.ent.name.." selected path start "..pathstartnode.name.." at "..minetest.pos_to_string(pathstartnode.pos))
|
||||
state.action.pos = vector.new(pathstartnode.pos)
|
||||
state.action.pos.y = state.action.pos.y + ANTP
|
||||
state.action.footpathdest = "path_"..state.action.name
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- No path node, so look for any area with the actual name and
|
||||
-- just go there.
|
||||
local nearest = areas:findNearestArea(state.pos, "^"..people.escape_pattern(state.action.name).."$")
|
||||
if not nearest then
|
||||
dbg.v2(state.ent.name.." couldn't find location "..state.action.name)
|
||||
return true, false
|
||||
if areas then
|
||||
local nearest = areas:findNearestArea(state.pos, "^"..people.escape_pattern(state.action.name).."$")
|
||||
if nearest then
|
||||
dbg.v2(state.ent.name.." set destination for centre of area "..nearest.name)
|
||||
state.action.pos = vector.interpolate(nearest.pos1, nearest.pos2, 0.5)
|
||||
state.action.pos.y = state.action.pos.y - 0.5
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
dbg.v2(state.ent.name.." set destination for centre of area "..nearest.name)
|
||||
state.action.pos = vector.interpolate(nearest.pos1, nearest.pos2, 0.5)
|
||||
state.action.pos.y = state.action.pos.y - 0.5
|
||||
return false
|
||||
|
||||
dbg.v2(state.ent.name.." couldn't find location "..state.action.name)
|
||||
return true, false
|
||||
end
|
||||
|
||||
if not state.action.pos and state.action.footpathdest then
|
||||
|
||||
-- path handling - only comes here when we've arrived at a new path square
|
||||
-- (note, the range passed to findNearestArea determines our lookahead distance
|
||||
-- (note, the range passed to nearest_node determines our lookahead distance
|
||||
dbg.v3(state.ent.name.." at new footpath node at "..minetest.pos_to_string(state.pos))
|
||||
local curpatharea, distance = areas:findNearestArea(state.pos, "^path_.+", nil, 10)
|
||||
if curpatharea and curpatharea.pos1.x == state.pos.x and curpatharea.pos1.z == state.pos.z then
|
||||
local curpathnode, distance = footpath.nearest_node(state.pos, nil, 10)
|
||||
if curpathnode and curpathnode.pos.x == state.pos.x and curpathnode.pos.z == state.pos.z then
|
||||
|
||||
if curpatharea.name == state.action.footpathdest.name then
|
||||
if curpathnode.name == state.action.footpathdest then
|
||||
dbg.v2(state.ent.name.." arrived at footpath destination")
|
||||
if state.action.endpos then
|
||||
state.action.footpathdest = nil
|
||||
|
@ -87,21 +86,37 @@ people.actions.go = function(state)
|
|||
end
|
||||
|
||||
-- We're at a junction
|
||||
dbg.v2(state.ent.name.." is at footpath junction "..curpatharea.name.." - looking for route to "..state.action.name)
|
||||
dbg.v2(state.ent.name.." is at footpath junction "..curpathnode.name.." - looking for route to "..state.action.name)
|
||||
state.action.lastpathpos = vector.new(state.pos.x, state.pos.y - PNTP, state.pos.z)
|
||||
local nextpathdest = people.get_footpath_route(curpatharea.name, "path_"..state.action.name)
|
||||
local nextpathdest = footpath.get_route(curpathnode.name, "path_"..state.action.name)
|
||||
if not nextpathdest then
|
||||
dbg.v2(state.ent.name.." can't find route")
|
||||
dbg.v1(state.ent.name.." can't find route")
|
||||
return true, false
|
||||
end
|
||||
local tonodename = "^to_"..string.sub(nextpathdest[2], 6).."$"
|
||||
nextpathdest = areas:findNearestArea(state.pos, tonodename)
|
||||
if not nextpathdest then
|
||||
dbg.v2(state.ent.name.." can't find first route node "..tonodename)
|
||||
local exitoff = nil
|
||||
for dir, exitto in pairs(curpathnode.neighbours) do
|
||||
if exitto.name == nextpathdest[2] then
|
||||
if dir == "n" then
|
||||
exitoff = {x=0, z=1}
|
||||
elseif dir == "s" then
|
||||
exitoff = {x=0, z=-1}
|
||||
elseif dir == "e" then
|
||||
exitoff = {x=1, z=0}
|
||||
elseif dir == "w" then
|
||||
exitoff = {x=-1, z=0}
|
||||
else
|
||||
dbg.v1(state.ent.name.." can't go in direction "..dir)
|
||||
return true, false
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
if not exitoff then
|
||||
dbg.v1(state.ent.name.." can't get exit offset from "..curpathnode.name.." to "..nextpathdest[2])
|
||||
return true, false
|
||||
end
|
||||
dbg.v2(state.ent.name.." leaving junction via "..nextpathdest.name)
|
||||
state.action.pos = vector.add(nextpathdest.pos1, {x=0, y=ANTP, z=0})
|
||||
dbg.v2(state.ent.name.." leaving junction towards "..nextpathdest[2])
|
||||
state.action.pos = vector.add(curpathnode.pos, {x=exitoff.x, y=ANTP, z=exitoff.z})
|
||||
return false
|
||||
|
||||
end
|
||||
|
@ -114,7 +129,7 @@ people.actions.go = function(state)
|
|||
-- extra 0.5 allowing for dodgy step-ups, for now...
|
||||
local thispathpos = vector.round({x=state.pos.x, y=state.pos.y + 0.5, z=state.pos.z})
|
||||
thispathpos.y = thispathpos.y - 1
|
||||
local nextpathpos = people.footpath_findnext(thispathpos, state.action.lastpathpos, false)
|
||||
local nextpathpos = footpath.findnext(thispathpos, state.action.lastpathpos, false)
|
||||
if nextpathpos == "unloaded" then
|
||||
state.wait = 1
|
||||
dbg.v3(state.ent.name.." waiting for block load")
|
||||
|
@ -130,7 +145,7 @@ people.actions.go = function(state)
|
|||
if not distance or distance > 10 then distance = 10 end
|
||||
while distance > 2 do
|
||||
-- Look ahead a bit while going in a straight line, to increase speed
|
||||
local aheadpathpos = people.footpath_findnext(nextpathpos, thispathpos, true)
|
||||
local aheadpathpos = footpath.findnext(nextpathpos, thispathpos, true)
|
||||
if aheadpathpos == "unloaded" then
|
||||
state.wait = 1
|
||||
dbg.v3(state.ent.name.." waiting for block load for lookahead")
|
||||
|
@ -174,16 +189,16 @@ people.actions.go = function(state)
|
|||
|
||||
-- If it's a long way to the destination position, see if we can find
|
||||
-- a route using a footpath instead.
|
||||
if distance > 30 and not state.action.footpathdest then
|
||||
local pathstartarea, psdist = areas:findNearestArea(state.pos, "^path_.+")
|
||||
if pathstartarea and psdist < distance then
|
||||
local pathdestarea, psdist = areas:findNearestArea(state.action.pos, "^path_.+")
|
||||
if pathdestarea and psdist < distance then
|
||||
if footpath and distance > 30 and not state.action.footpathdest then
|
||||
local pathstartnode = footpath.nearest_node(state.pos, nil, distance)
|
||||
if pathstartnode then
|
||||
local pathdestnode = footpath.nearest_node(state.action.pos, nil, distance)
|
||||
if pathdestarea then
|
||||
state.action.endpos = state.action.pos
|
||||
state.action.pos = vector.new(pathstartarea.pos1)
|
||||
state.action.pos = vector.new(pathstartnode.pos)
|
||||
state.action.pos.y = state.action.pos.y + ANTP
|
||||
state.action.footpathdest = pathdestarea
|
||||
dbg.v2(state.ent.name.." using footpath from "..pathstartarea.name.." to "..pathdestarea.name.." for intermediate route")
|
||||
state.action.footpathdest = pathdestnode.name
|
||||
dbg.v2(state.ent.name.." using footpath from "..pathstartnode.name.." to "..pathdestnode.name.." for intermediate route")
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
|
|
@ -64,7 +64,7 @@ subcmd.footpath_update = {
|
|||
return false, "Only admins can update the footpath node graph"
|
||||
end
|
||||
|
||||
response = people.footpath_make_pathnodes()
|
||||
response = footpath.make_pathnodes()
|
||||
if response then
|
||||
return false, response
|
||||
end
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
areas?
|
||||
footpath?
|
||||
mod_debug?
|
||||
|
|
301
footpath.lua
301
footpath.lua
|
@ -1,301 +0,0 @@
|
|||
|
||||
local dbg
|
||||
if moddebug then dbg=moddebug.dbg("people") else dbg={v1=function() end,v2=function() end,v3=function() end} end
|
||||
|
||||
local pathnodes = nil
|
||||
local routecache = {}
|
||||
|
||||
--- Save the current footpath network status.
|
||||
people.footpath_save = function()
|
||||
if not pathnodes then return end
|
||||
|
||||
-- For the purposes of serialisation, we want a copy of the nodes with
|
||||
-- neighbours referenced by node name, not a reference to the actual
|
||||
-- node, otherwise it will serialise horribly!
|
||||
local lnodes = {}
|
||||
for n, nn in pairs(pathnodes) do
|
||||
local nei = {}
|
||||
for _, ne in pairs(nn.neighbours) do
|
||||
table.insert(nei, ne.name)
|
||||
end
|
||||
lnodes[n] = {name=n, pos=nn.pos, neighbours=nei}
|
||||
end
|
||||
|
||||
local f = io.open(minetest.get_worldpath().."/people_footpaths.json", "w+")
|
||||
if f then
|
||||
f:write(minetest.write_json(lnodes))
|
||||
f:close()
|
||||
end
|
||||
end
|
||||
|
||||
--- Load the footpath network status.
|
||||
people.footpath_load = function()
|
||||
local f = io.open(minetest.get_worldpath().."/people_footpaths.json", "r")
|
||||
if f then
|
||||
pathnodes = minetest.parse_json(f:read("*all"))
|
||||
f:close()
|
||||
|
||||
-- Now we need to undo the dereferencing we did when we saved it...
|
||||
for _, nn in pairs(pathnodes) do
|
||||
local nei = {}
|
||||
if nn.neighbours then
|
||||
for _, ne in pairs(nn.neighbours) do
|
||||
table.insert(nei, pathnodes[ne])
|
||||
end
|
||||
end
|
||||
nn.neighbours = nei
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
--- Build the path nodes graph.
|
||||
-- This is intended to be called via a chat command by a server administrator.
|
||||
-- It builds a graph by interrogating the defined areas. This graph is then
|
||||
-- used when finding paths.
|
||||
-- @return A string describing an error in the setup (in which case no changes
|
||||
-- are made), or nil on success (in which case, the new graph will
|
||||
-- take effect).
|
||||
people.footpath_make_pathnodes = function()
|
||||
|
||||
newpathnodes = {}
|
||||
local allnodes = areas:getAreas({x=0,y=0,z=0}, "^path_.*", nil)
|
||||
for _, pn in pairs(allnodes) do
|
||||
if not vector.equals(pn.pos1, pn.pos2) then
|
||||
return "Path node "..pn.name.." is the wrong size"
|
||||
end
|
||||
newpathnodes[pn.name] = {name=pn.name, pos=pn.pos1, neighbours={}}
|
||||
end
|
||||
|
||||
for _, pn in ipairs(allnodes) do
|
||||
local exits = areas:getAreas(pn.pos1, "^to_.*", 1.5)
|
||||
for _, tn in pairs(exits) do
|
||||
if tn.name ~= "to_*" then
|
||||
exname = tn.name:sub(4)
|
||||
if not newpathnodes["path_"..exname] then
|
||||
return "Exit "..exname.." at "..minetest.pos_to_string(tn.pos1).." has no destination"
|
||||
end
|
||||
table.insert(newpathnodes[pn.name].neighbours, newpathnodes["path_"..exname])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _, pn in pairs(newpathnodes) do
|
||||
for _, nn in pairs(pn.neighbours) do
|
||||
local ok = false
|
||||
for _, nnn in pairs(nn.neighbours) do
|
||||
if nnn == pn then
|
||||
ok = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not ok then
|
||||
return "Route from "..pn.name.." at "..minetest.pos_to_string(pn.pos)..
|
||||
" to "..nn.name.." at "..minetest.pos_to_string(nn.pos)..
|
||||
" does not return"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
pathnodes = newpathnodes
|
||||
routecache = {}
|
||||
people.footpath_save()
|
||||
return nil
|
||||
|
||||
end
|
||||
|
||||
--- Fast removal of a value from a table
|
||||
-- Avoids shuffling by swapping the last item with the one to be removed.
|
||||
-- @param t The table
|
||||
-- @param v The value to remove
|
||||
local function table_fast_remove(t, v)
|
||||
for i, tv in ipairs(t) do
|
||||
if tv == v then
|
||||
t[i] = t[#t]
|
||||
t[#t] = nil
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Check if a table contains the given value
|
||||
-- @param t The table
|
||||
-- @param v The value to check for
|
||||
-- @return True if the table contains the value
|
||||
local function table_contains(t, v)
|
||||
|
||||
for _, tv in ipairs(t) do
|
||||
if tv == v then return true end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
--- Choose the best node from the list of open ones
|
||||
-- @param open List of nodes
|
||||
-- @param f Corresponding list of f scores for those node
|
||||
-- @return The node with the lowest f score
|
||||
local function choose_best(open, f)
|
||||
|
||||
local lowest, best = 1/0, nil
|
||||
for _, node in ipairs(open) do
|
||||
local ts = f[node]
|
||||
if ts < lowest then
|
||||
lowest, best = ts, node
|
||||
end
|
||||
end
|
||||
return best
|
||||
end
|
||||
|
||||
--- Reverse-iterate along the found route to construct the final path.
|
||||
local function get_path(path, prev, current)
|
||||
|
||||
if not prev[current] then return path end
|
||||
table.insert(path, 1, prev[current])
|
||||
return get_path(path, prev, prev[current])
|
||||
end
|
||||
|
||||
--- Use A* to calculate the route from start to goal. Paths are cached.
|
||||
local function astar(start, goal)
|
||||
|
||||
if not routecache[start] then
|
||||
routecache[start] = {}
|
||||
elseif routecache[start][goal] then
|
||||
dbg.v3("astar used cached path")
|
||||
return routecache[start][goal]
|
||||
end
|
||||
|
||||
local g = {[start]=0}
|
||||
local f = {[start]=vector.distance(start.pos, goal.pos)}
|
||||
local open = {start}
|
||||
local closed = {}
|
||||
local prev = {}
|
||||
|
||||
while #open > 0 do
|
||||
|
||||
local current = choose_best(open, f)
|
||||
if current == goal then
|
||||
local path = get_path({}, prev, goal)
|
||||
table.insert(path, goal)
|
||||
routecache[start][goal] = path
|
||||
return path
|
||||
end
|
||||
|
||||
table_fast_remove(open, current)
|
||||
table.insert(closed, current)
|
||||
|
||||
for _, neighbour in ipairs(current.neighbours) do
|
||||
if not table_contains(closed, neighbour) then
|
||||
|
||||
local newg = g[current] + vector.distance(current.pos, neighbour.pos)
|
||||
|
||||
if not table_contains(open, neighbour) or newg < g[neighbour] then
|
||||
prev[neighbour] = current
|
||||
g[neighbour] = newg
|
||||
f[neighbour] = g[neighbour] + vector.distance(neighbour.pos, goal.pos)
|
||||
if not table_contains(open, neighbour) then
|
||||
table.insert(open, neighbour)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Get a footpath route
|
||||
-- @param start Starting node name, e.g. "path_My House"
|
||||
-- @param goal Goal node name, e.g. "path_Your House"
|
||||
-- @return A list of path node names defining the route, including the start
|
||||
-- and the goal, or nil if no route was found.
|
||||
people.get_footpath_route = function(start, goal)
|
||||
|
||||
if not pathnodes then return nil end
|
||||
if not pathnodes[start] then
|
||||
dbg.v3("Route start "..start.." doesn't exist")
|
||||
return nil
|
||||
end
|
||||
if not pathnodes[goal] then
|
||||
dbg.v3("Route goal "..goal.." doesn't exist")
|
||||
return nil
|
||||
end
|
||||
|
||||
path = astar(pathnodes[start], pathnodes[goal])
|
||||
if not path then
|
||||
dbg.v3("Route from "..start.." to "..goal.." not found")
|
||||
return nil
|
||||
end
|
||||
|
||||
rpath = {}
|
||||
for _, n in ipairs(path) do
|
||||
table.insert(rpath, n.name)
|
||||
end
|
||||
dbg.v3("Route from "..start.." to "..goal.." is "..dump(rpath))
|
||||
return rpath
|
||||
|
||||
end
|
||||
|
||||
--- Find the next node along a footpath
|
||||
-- All positions are exact (rounded) node positions.
|
||||
-- @param curpos The position of a current footpath node
|
||||
-- @param lastpos The position of the previous footpath node
|
||||
-- @param samedironly True to only look in the current direction of travel
|
||||
-- @return The position of the next footpath node, or nil if there isn't
|
||||
-- one, or "unloaded" if there may or may not be one, but we can't
|
||||
-- tell because of unloaded blocks.
|
||||
people.footpath_findnext = function(curpos, lastpos, samedironly)
|
||||
--dbg.v3("footpath_findnext, cur="..minetest.pos_to_string(curpos)..", last="..minetest.pos_to_string(lastpos)..", same="..dump(samedironly))
|
||||
local xz
|
||||
if samedironly then
|
||||
xz = {}
|
||||
else
|
||||
xz = {{x=1,z=0}, {x=0,z=1}, {x=-1,z=0}, {x=0,z=-1}}
|
||||
end
|
||||
-- Favour the direction we're already going
|
||||
-- TODO - that means we could check it twice (but then again, only
|
||||
-- if we reach an invalid bit of footpath!
|
||||
table.insert(xz, 1, {x=curpos.x-lastpos.x, z=curpos.z-lastpos.z})
|
||||
for _, cxz in ipairs(xz) do
|
||||
for y = 1, -1, -1 do
|
||||
local x = cxz.x
|
||||
local z = cxz.z
|
||||
local npos = vector.add(curpos, vector.new(x, y, z))
|
||||
if not vector.equals(npos, lastpos) then
|
||||
local n = minetest.get_node(npos)
|
||||
if n.name == "ignore" then return "unloaded" end
|
||||
if n.name == 'default:cobble' or
|
||||
n.name == 'default:mossycobble' or
|
||||
n.name == 'stairs:stair_cobble' or
|
||||
n.name == 'default:wood' or
|
||||
n.name == 'stairs:stair_wood' then
|
||||
local n = minetest.get_node({x=npos.x,y=npos.y+1,z=npos.z})
|
||||
if n.name == "default:snow" then
|
||||
-- Snow is walkable, but we will either walk over it
|
||||
-- or dig it out of the way, so that's fine...
|
||||
--dbg.v3("footpath_findnext found (snow-covered) "..minetest.pos_to_string(npos))
|
||||
return npos
|
||||
end
|
||||
|
||||
-- Otherwise, so long as there's nothing walkable above the
|
||||
-- node it should be a valid footpath node...
|
||||
-- TODO - allowing doors/gates here, as we do elsewhere, but
|
||||
-- really we need to know if the entity can use doors!
|
||||
local nd = minetest.registered_nodes[n.name]
|
||||
local iswalkable = nd.walkable
|
||||
if iswalkable and (
|
||||
(nd.groups.door and nd.groups.door ~= 0) or
|
||||
(nd.groups.gate and nd.groups.gate ~= 0)) then
|
||||
iswalkable = false
|
||||
end
|
||||
|
||||
if not iswalkable then
|
||||
--dbg.v3("footpath_findnext found "..minetest.pos_to_string(npos))
|
||||
return npos
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
3
init.lua
3
init.lua
|
@ -9,7 +9,6 @@ local people_modpath = minetest.get_modpath("people")
|
|||
dofile(people_modpath .. "/commands.lua")
|
||||
dofile(people_modpath .. "/tracking.lua")
|
||||
dofile(people_modpath .. "/form.lua")
|
||||
dofile(people_modpath .. "/footpath.lua")
|
||||
|
||||
|
||||
-- Contains all action handlers. Each action handler takes a 'state' table as
|
||||
|
@ -33,8 +32,6 @@ dofile(people_modpath .. "/actions/pickup.lua")
|
|||
dofile(people_modpath .. "/actions/rightclicknode.lua")
|
||||
dofile(people_modpath .. "/actions/harvest.lua")
|
||||
|
||||
people.footpath_load()
|
||||
|
||||
people.presets = {}
|
||||
for p, priv in pairs({FollowOwner=false,
|
||||
SimpleCommands=false,
|
||||
|
|
|
@ -21,8 +21,11 @@ if event.type == "program" then
|
|||
"farming_plus:orange_seed 20",
|
||||
"farming_plus:potato_seed 20",
|
||||
"farming_plus:rhubarb_seed 20",
|
||||
"farming:pumpkin_seed 20",
|
||||
"farming_plus:strawberry_seed 20"}},
|
||||
{"go", name="Ciaran's Farm"},
|
||||
-- Inside gate
|
||||
{"go", pos={x=174, y=11.5, z=320}},
|
||||
|
||||
-- Row 1
|
||||
|
||||
|
@ -85,8 +88,16 @@ if event.type == "program" then
|
|||
{"wait", time=5},
|
||||
{"gather"},
|
||||
|
||||
-- Pumpkin patch
|
||||
|
||||
{"go", pos={x=194, y=11.5, z=322}},
|
||||
{"go", pos={x=202, y=11.5, z=322}},
|
||||
{"gather", nodes={"farming:weed", "farming:pumpkin"}, plant="farming:pumpkin_seed"},
|
||||
{"wait", time=5},
|
||||
{"gather"},
|
||||
-- Finished. Save seed stock...
|
||||
|
||||
{"go", pos={x=174, y=11.5, z=320}},
|
||||
{"go", name="Ciaran's Farm"},
|
||||
{"wait", time=10},
|
||||
|
||||
|
@ -99,12 +110,13 @@ if event.type == "program" then
|
|||
"farming_plus:potato_seed 20",
|
||||
"farming_plus:orange_seed 20",
|
||||
"farming_plus:rhubarb_seed 20",
|
||||
"farming:pumpkin_seed 20",
|
||||
"farming_plus:strawberry_seed 20"}},
|
||||
|
||||
-- Load up items...
|
||||
|
||||
{"go", name="Ciaran's Farm"},
|
||||
{"go", pos={x=174, y=11.5, z=319}},
|
||||
{"go", pos={x=174, y=11.5, z=320}},
|
||||
|
||||
{"go", pos={x=174, y=11.5, z=341}},
|
||||
{"stash", dest="default:chest", items={"*farming:weed", "*farming:cotton",
|
||||
|
@ -117,7 +129,7 @@ if event.type == "program" then
|
|||
"*farming_plus:orange_item",
|
||||
"*farming_plus:strawberry_item", "*farming_plus:strawberry_seed"}},
|
||||
|
||||
{"go", pos={x=174, y=11.5, z=319}},
|
||||
{"go", pos={x=174, y=11.5, z=320}},
|
||||
|
||||
-- *****************
|
||||
-- Tree Farm
|
||||
|
|
Loading…
Reference in New Issue