A ton of stuff

master
Ciaran Gultnieks 2015-05-19 23:29:08 +01:00
parent fac95b47af
commit a5c2ff2ee1
17 changed files with 1330 additions and 163 deletions

131
README.md
View File

@ -134,7 +134,14 @@ it fails to do so, the person will do an automatic wait.
### actfail
This happens when an action fails.
This happens when an action fails, unless there is a 'prevaction' set. (To
get a fail, but still have a follow-on action, use 'successaction' instead!)
There is also event.failedaction available, which is the action that actually
failed, and event.failcode which is the failure code that the action returned.
That is normally just false, but some action handlers can return special
values here (anything non-true would be a fail) to indicate particular
things.
### tell
@ -186,14 +193,21 @@ position.
The following actions are currently defined:
#### go, with pos={x,y,z}
Walks to the given position.
#### go
#### go, with name="string"
Walks to a named position - names are looked up using the areas module, if
present This converts to go, pos={x,y,z}, with name retained for reference.
Go (walk) to a location. This generally decomposes into a more complex set
of actions, the ultimate aim of which is to get to the given destination
somehow.
Names are most frequently used in conjunction with footpaths.
One of the following parameters is needed:
* pos={x,y,z} - specifies an absolute position to go to
* rel={x,y,z} - to go a relative offset from the current position
* name=string - specifies a footpath node or area (see the Navigation
section
Many other parameters are handled by the go action, but these are used
internally only.
#### follow, with name="playername"
Follows the given player indefinitely (or at least until they log off).
@ -220,12 +234,17 @@ Dig the node at the given position. Must be within range.
#### rightclicknode, with pos={x,y,z}
Right-click the node at the given position. Must be within range.
#### place, with pos={x,y,z} and item=name
Places the given item. Must be in the inventory, and in range.
#### place
Places an item (node). Must be in the inventory, and in range.
* Required parameter: pos={x,y,z}
* Required parameter: item=itemname
* Optional parameter: param2=value (e.g. for setting facedir)
* Optional parameter: againstdir={x,y,z}
#### stash
* Required parameter: items={"item1", ..."itemn"}
* Required parameter: items={item1, ..., itemn"}
* Optional parameter: pos={x,y,z} - position of node to stash in
* Optional parameter: dest="node" - node name to stash in
(e.g. "default:chest")
@ -237,22 +256,24 @@ Specifically, it needs to be a node with a "main" inventory list. So for a
normal chest, use "default:chest" for the nodename.
Each item in the list can be:
* Just the item name (e.g. "default:cobble") to put one item in
* Just the item name (e.g. "default:cobble") to put all held in
* Item name and count (e.g. "default:cobble 50") to put that many in
* Item name preceded by an asterisk (e.g. "\*default:cobble") , to put all
carried in
* Table ({"default:cobble", keep=5}) to keep 5 but put the rest in
#### retrieve, with src="nodename" and items={"item1", "itemn"}
#### retrieve
Gets items from a source node, which should be something like a chest.
Specifically, it needs to be a node with a "main" inventory list. So for a
normal chest, use "default:chest" for the nodename.
Each item in the list can be:
* Just the item name (e.g. "default:cobble") to get one item out
* Item name and count (e.g. "default:cobble 50") to get that many out
* Item name preceded by an asterisk (e.g. "\*default:cobble") , to get all
of that kind of item out
* Required parameter: items={item1, ..., itemn"}
* Optional parameter: pos={x,y,z} - position of node to stash in
* Optional parameter: src="node" - node name to retrieve from
(e.g. "default:chest")
(You need either pos or dest, or nothing will happen)
The items in the list are defined in the same way as for the stash action.
#### sell
@ -266,13 +287,32 @@ afford to buy it all.
#### buy
* Required parameter: item="itemstring"
* Required parameter: item="itemname"
* Required parameter: num=amount
Need to be standing within range of a shop (barrel mod) that sells the item
in question, and have enough money. If the person doesn't have enough money
to buy the amount specified, a lesser amount may be bought instead.
#### obtain
Meta-action to obtain some of an item by whatever means possible. This could
use crafting, buying, mining, whatever, the only aim is to get it.
The specified amount is the total amount we want to end up having - so if we
already have that many, the action will not do anything. Otherwise, it could
use one or more of the available methods until the total is achieved.
* Required paramter: item="itemname"
* Required parameter: num=amount
#### craft
Craft an item.
* Required paramter: item="itemname"
* Required parameter: num=amount
#### gather
Changes current gathering settings.
@ -322,6 +362,57 @@ The correct item should already be wielded. This is normally used in
conjunction with gathering, in which case the wielding is already
handled.
#### tunnel
Tunnel, from the current position. Also builds bridges!
Parameters:
* `dir = {x,y,z}` - direction - either x or z must be 0. and the
other +/-1, while y can be 0 or +/-1.
* 'distance = n` - distance to go for (reduces as the action
proceeds)
* `floor=item` - material to use for floor - optional
* `wall=item` - material to use for wall linings - optional
* `ceiling=item` - material to use for ceiling - optional
* `steps=item` - material to use for steps, on slopes.
* `lighting=item` - material (most likely default:torch) to use for
lighting, which is placed against walls at
4 metre intervals. Optional.
* `support_top=item` - material to use for top supports, which are placed
against the ceiling at 8 metre intervals. Optional.
* `support_ceiling=item` - like support_top, but at ceiling level, not below.
* `support_side=item` - material to use for side supports. Optional.
* `support_side_facein=? - set to true to make the side supports face in
towards the tunnel centre. (Try it with slabs, for
example!)
* `support_wall=item` - like support_side, but at wall level, not within.
#### building
Make a building, from current position.
The current position is designated as the entrance to the building.
Parameters:
* `dir = {x,y,z}` - direction - either x or z must be 0. and the
other +/-1, while y can be 0 or +/-1.
* `width = n` - the width of the building
* `depth = n` - the depth of the building
* `height = n` - the height of the building
* `wall = item` - material to use for walls
* `outside_floor = item` - material to use for the floor outside the walls.
Optional. Does not include the entrance.
This can also be a table, with two entries, in
which case the floor can be either of those, and
if it's not it's replaced with the first. (Example
use case of that is if it's dirt, in which case
it can change to dirt_with_grass, and be left
alone!)
* `door=item` - Door to use. Doorways will be left open if not
specified.
#### step
Allow's the person's lua code to handle on_step itself. It will receive
"step" events (with dtime) and should reset the action when it no longer

213
actions/building.lua Normal file
View File

@ -0,0 +1,213 @@
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

View File

@ -75,6 +75,7 @@ people.actions.buy = function(state)
local repeats = math.ceil(state.action.num / shopinfo.saleunit)
local cashneeded = repeats * shopinfo.price
dbg.v3(state.ent.name.." trying to put $"..cashneeded.." into shop")
if not currency.remove_from_inv(state.ent.inventory, "main", cashneeded) then
dbg.v1(state.ent.name.." doesn't have enough money to buy")
return true, false
@ -83,8 +84,14 @@ people.actions.buy = function(state)
-- to check and roll back, as we do for getting money
currency.put_to_inv(shopinv, "exchange", cashneeded)
local bought = 0
while repeats > 0 do
if not barrel.do_buy(shopinfo.pos) then break end
local success, msg = barrel.do_buy(shopinfo.pos)
if not success then
dbg.v3(state.ent.name.." failed during buy loop - shop said: "..msg)
break
end
bought = bought + shopinfo.saleunit
repeats = repeats - 1
end
@ -92,12 +99,12 @@ people.actions.buy = function(state)
dbg.v1(state.ent.name.." couldn't retrieve everything from shop inventory")
end
dbg.v1(state.ent.name.." finished buy action")
dbg.v1(state.ent.name.." finished buy action - bought "..bought.." "..state.action.item)
return true, true
end
people.actions.sell = function(state)
if not (state.action.item) then
if not state.action.item then
dbg.v1(state.ent.name.." has invalid sell action")
return true, false
end

39
actions/claimplot.lua Normal file
View File

@ -0,0 +1,39 @@
local dbg
if moddebug then dbg=moddebug.dbg("people") else dbg={v1=function() end,v2=function() end,v3=function() end} end
people.actions.claimplot = function(state)
if not state.action.name or type(state.action.name) ~= "string" then
dbg.v1(state.ent.name.." has invalid claimplot action")
return true, false
end
local destpos = footpath.get_nodepos(name)
if destpos then
dbg.v1(state.ent.name.." can't claim "..state.action.name.." - it already exists")
return true, false
end
if not state.action.at then
local plot = footpath.nearest_vacant_plot(state.pos)
if not plot then
dbg.v1(state.ent.name.." can't claim "..state.action.name.." - there are no free plots")
return true, false
end
state.action.at = plot.name
state.action = {"go", name=plot.name, successaction=state.action}
return false
end
local result = footpath.claim_plot(state.action.at, state.action.name)
if result ~= true then
dbg.v1(state.ent.name.." failed to claim plot "..state.action.name.." at "..state.action.at.." - "..result)
return true, false
end
dbg.v1(state.ent.name.." claimed plot "..state.action.name.." at "..state.action.at)
return true, true
end

80
actions/craft.lua Normal file
View File

@ -0,0 +1,80 @@
local dbg
if moddebug then dbg=moddebug.dbg("people") else dbg={v1=function() end,v2=function() end,v3=function() end} end
people.actions.craft = function(state)
if type(state.action.num) ~= "number" then
dbg.v1(state.ent.name.." has invalid num for craft action")
return true, false
end
if type(state.action.item) ~= "string" then
dbg.v1(state.ent.name.." has invalid item for craft action")
return true, false
end
-- TODO check it's a real item too!
local recipes = minetest.get_all_craft_recipes(state.action.item)
if not recipes or #recipes == 0 then
dbg.v2(state.ent.name.." can't craft "..state.action.item.. " - no recipes")
return true, false
end
local tomake = state.action.num
for _, recipe in pairs(recipes) do
dbg.v3(state.ent.name.." trying recipe "..minetest.serialize(recipe))
if recipe.type == "normal" then
local output = ItemStack(recipe.output)
local needed = {}
for _, item in pairs(recipe.items) do
-- TODO - we can't handle groups in recipes here yet, so
-- we'll just translate things as we come across
-- them for now.
if item == "group:stick" then
item = "default:stick"
elseif item == "group:wood" then
item = "default:wood"
end
if needed[item] then
needed[item] = needed[item] + 1
else
needed[item] = 1
end
end
while tomake > 0 do
local gotingredients = true
for item, count in pairs(needed) do
if not state.ent.inventory:contains_item("main", ItemStack(item.." "..count)) then
dbg.v3(state.ent.name.." doesn't have the "..count.." "..item.." needed")
gotingredients = false
break
end
end
if not gotingredients then break end
for item, count in pairs(needed) do
state.ent.inventory:remove_item("main", ItemStack(item.." "..count))
end
state.ent.inventory:add_item("main", output)
dbg.v3(state.ent.name.." crafted "..output:get_count().." "..state.action.item)
tomake = tomake - output:get_count()
end
else
dbg.v3(state.ent.name.." doesn't know how to do '"..recipe.type.."'")
end
end
if tomake <= 0 then
dbg.v1(state.ent.name.." successfully crafted "..state.action.num.." "..state.action.item)
return true, true
end
dbg.v1(state.ent.name.." couldn't craft (any or enough) "..state.action.item)
return true, false
end

View File

@ -40,6 +40,7 @@ people.actions.dig = function(state)
-- * selecting correct tool
-- * proper drops (e.g. custom handlers in other mods)
local n = minetest.get_node(state.action.pos)
local dign = n.name
if false then
@ -94,7 +95,7 @@ people.actions.dig = function(state)
return true, false
end
end
dbg.v1(state.ent.name.." dug "..n.name.." at "..minetest.pos_to_string(state.action.pos))
dbg.v1(state.ent.name.." dug "..dign.." at "..minetest.pos_to_string(state.action.pos))
return true, true

View File

@ -14,6 +14,12 @@ local PNTP = 0.5
people.actions.go = function(state)
if state.action.rel then
state.action.pos = vector.add(vector.round(state.pos), state.action.rel)
dbg.v2(state.ent.name.." converted relative go to destination "..minetest.pos_to_string(state.action.pos))
state.action.rel = nil
end
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
@ -35,7 +41,7 @@ people.actions.go = function(state)
-- 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)
local pathstartnode = footpath.nearest_junction(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)
@ -65,9 +71,9 @@ people.actions.go = function(state)
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 nearest_node determines our lookahead distance
-- (note, the range passed to nearest_junction determines our lookahead distance
dbg.v3(state.ent.name.." at new footpath node at "..minetest.pos_to_string(state.pos))
local curpathnode, distance = footpath.nearest_node(state.pos, nil, 10)
local curpathnode, distance = footpath.nearest_junction(state.pos, nil, 10)
if curpathnode and curpathnode.pos.x == state.pos.x and curpathnode.pos.z == state.pos.z then
if curpathnode.name == state.action.footpathdest then
@ -77,6 +83,36 @@ people.actions.go = function(state)
state.action.pos = state.action.endpos
return false
end
if state.action.faceexit then
local jct = footpath.get_junction(curpathnode.name)
if not jct then
dbg.v1(state.ent.name.." couldn't do faceexit at "..curpathnode.name.." - no such junction")
return true, false
end
local fdir = nil
for dir, exit in pairs(jct.exits) do
if exit == state.action.faceexit then
fdir = dir
break
end
end
if not fdir then
dbg.v1(state.ent.name.." couldn't do faceexit at "..curpathnode.name.." - no such exit")
return truen, false
end
local fpos
if fdir == "n" then
fpos = vector.add(state.pos, {x=0, y=0, z=1})
elseif fdir == "s" then
fpos = vector.add(state.pos, {x=0, y=0, z=-1})
elseif fdir == "e" then
fpos = vector.add(state.pos, {x=1, y=0, z=0})
else
fpos = vector.add(state.pos, {x=-1, y=0, z=0})
end
state.yaw = vector.get_yaw(state.pos, fpos)
dbg.v2(state.ent.name.." set faceexit yaw of "..state.yaw)
end
return true, true
end
@ -129,7 +165,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 = footpath.findnext(thispathpos, state.action.lastpathpos, false)
local nextpathpos, digit = footpath.findnext(thispathpos, state.action.lastpathpos, false)
if nextpathpos == "unloaded" then
state.wait = 1
dbg.v3(state.ent.name.." waiting for block load")
@ -143,15 +179,15 @@ people.actions.go = function(state)
end
if not distance or distance > 10 then distance = 10 end
while distance > 2 do
while distance > 2 and not digit do
-- Look ahead a bit while going in a straight line, to increase speed
local aheadpathpos = footpath.findnext(nextpathpos, thispathpos, true)
local aheadpathpos, digit = footpath.findnext(nextpathpos, thispathpos, true)
if aheadpathpos == "unloaded" then
state.wait = 1
dbg.v3(state.ent.name.." waiting for block load for lookahead")
return false
end
if aheadpathpos then
if aheadpathpos and not digit then
thispathpos = nextpathpos
nextpathpos = aheadpathpos
distance = distance - 1
@ -161,6 +197,10 @@ people.actions.go = function(state)
end
state.action.lastpathpos = thispathpos
state.action.pos = vector.add(nextpathpos, {x=0, y=0.5, z=0})
if digit then
dbg.v3(state.ent.name.." clearing snow from path at "..minetest.pos_to_string(state.action.pos))
state.action={"go", pos=state.action.pos, prevaction={"dig", pos=vector.add(nextpathpos, {x=0, y=1, z=0}), prevaction=state.action}}
end
dbg.v3(state.ent.name.." set next footpath dest "..minetest.pos_to_string(state.action.pos))
return false
@ -190,9 +230,9 @@ 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 footpath and distance > 30 and not state.action.footpathdest then
local pathstartnode = footpath.nearest_node(state.pos, nil, distance)
local pathstartnode = footpath.nearest_junction(state.pos, nil, distance)
if pathstartnode then
local pathdestnode = footpath.nearest_node(state.action.pos, nil, distance)
local pathdestnode = footpath.nearest_junction(state.action.pos, nil, distance)
if pathdestarea then
state.action.endpos = state.action.pos
state.action.pos = vector.new(pathstartnode.pos)
@ -271,66 +311,70 @@ people.actions.go = function(state)
if ahead > 1 then ahead = 1 end
local nd = vector.from_speed_yaw(ahead, state.yaw)
local nextpos = vector.add(vector.round(state.pos), nd)
nextpos.y = nextpos.y + 2
-- How far down we'll look, which needs to be at least this to
-- be able to descend stairs.
local ncount = 6
local diags = "at:" ..minetest.pos_to_string(state.pos)
local notwalkable = 0
local unloaded = false
diags = diags .. " next+1:" ..minetest.pos_to_string(nextpos)
while ncount > 0 do
local nn = minetest.get_node(nextpos).name
diags = diags .. ":" .. nn
if nn == "ignore" then
unloaded = true
break
end
nd = minetest.registered_nodes[nn]
if not nd then
erk = "mysterious " .. nn
break
end
local gg = nd.groups or {}
if gg.water or gg.lava then
erk = nn
break
end
-- Don't look at the ahead position if it's the same node we're
-- already at...
if not vector.equals(vector.round(state.pos), nextpos) then
nextpos.y = nextpos.y + 2
-- How far down we'll look, which needs to be at least this to
-- be able to descend stairs.
local ncount = 6
local diags = "at:" ..minetest.pos_to_string(state.pos)
local notwalkable = 0
local unloaded = false
diags = diags .. " next+1:" ..minetest.pos_to_string(nextpos)
while ncount > 0 do
local nn = minetest.get_node(nextpos).name
diags = diags .. ":" .. nn
if nn == "ignore" then
unloaded = true
break
end
nd = minetest.registered_nodes[nn]
if not nd then
erk = "mysterious " .. nn
break
end
local gg = nd.groups or {}
if gg.water or gg.lava then
erk = nn
break
end
-- Use 'walkable' to decide if we will bump into, and/or can stand
-- on the node. But with a bit of variation. We ignore doors so we
-- bump into them on purpose!?
local iswalkable = nd.walkable
if iswalkable and state.ent.can_use_doors and (
(nd.groups.door and nd.groups.door ~= 0) or
(nd.groups.gate and nd.groups.gate ~= 0)) then
iswalkable = false
end
-- Use 'walkable' to decide if we will bump into, and/or can stand
-- on the node. But with a bit of variation. We ignore doors so we
-- bump into them on purpose!?
local iswalkable = nd.walkable
if iswalkable and state.ent.can_use_doors and (
(nd.groups.door and nd.groups.door ~= 0) or
(nd.groups.gate and nd.groups.gate ~= 0)) then
iswalkable = false
end
if iswalkable and ncount < 6 then
break
if iswalkable and ncount < 6 then
break
end
if not iswalkable then
notwalkable = notwalkable + 1
end
nextpos.y = nextpos.y - 1
ncount = ncount - 1
end
if not iswalkable then
notwalkable = notwalkable + 1
if unloaded then
-- If we hit an unloaded block, just wait until it loads.
dbg.v3(state.ent.name.." waiting for unloaded to check ahead")
state.wait = 1
return false
end
if ncount == 0 then
erk = "fall"
elseif notwalkable < 2 then
erk = "bump"
end
if erk then
dbg.v1(state.ent.name.." aborted go due to fear of " ..
erk .. " - " .. diags)
unwholesome = true
end
nextpos.y = nextpos.y - 1
ncount = ncount - 1
end
if unloaded then
-- If we hit an unloaded block, just wait until it loads.
dbg.v3(state.ent.name.." waiting for unloaded to check ahead")
state.wait = 1
return false
end
if ncount == 0 then
erk = "fall"
elseif notwalkable < 2 then
erk = "bump"
end
if erk then
dbg.v1(state.ent.name.." aborted go due to fear of " ..
erk .. " - " .. diags)
unwholesome = true
end
end
@ -338,8 +382,10 @@ people.actions.go = function(state)
if distance < 32 then
if state.action.triedpath then
dbg.v2(state.ent.name.." is not trying a path to "..minetest.pos_to_string(curdest).." again")
return true, false
state.action.triedpath = false
dbg.v2(state.ent.name.." is waiting before trying a path again ")
state.action = {"wait", time=30, prevaction=state.action}
return false
end
local spos = vector.new(state.pos)

67
actions/obtain.lua Normal file
View File

@ -0,0 +1,67 @@
local dbg
if moddebug then dbg=moddebug.dbg("people") else dbg={v1=function() end,v2=function() end,v3=function() end} end
people.actions.obtain = function(state)
if type(state.action.num) ~= "number" then
dbg.v1(state.ent.name.." has invalid num for obtain action")
return true, false
end
if type(state.action.item) ~= "string" then
dbg.v1(state.ent.name.." has invalid item for obtain action")
return true, false
end
-- TODO check it's a real item too!
-- See how many we've got, and therefore how many we need...
local inv = state.ent.inventory
local count = 0
for i, stack in ipairs(inv:get_list("main")) do
local nn = stack:get_name()
if nn == state.action.item then
count = count + stack:get_count()
end
end
local needed = state.action.num - count
if needed <= 0 then
dbg.v2(state.ent.name.." has "..count.." "..state.action.item)
return true, true
end
if not state.action.crafted then
state.action.crafted = true
state.action = {"craft", item=state.action.item, num=needed,
prevaction=state.action}
return false
end
if not state.action.bought then
state.action.bought = true
-- TODO - Here is where we need knowledge or memory of what goods can
-- be purchased where. Before we have that, we're just going to hard
-- code a few things to test the concept...
local where = nil
if state.action.item == "default:wood" then
where = "Building Supplies 2"
elseif state.action.item == "default:stone" then
where = "Building Supplies 1"
end
if where then
state.action = {"go", name=where, prevaction=
{"buy", item=state.action.item, num=needed, prevaction=
state.action}}
return false
else
dbg.v3(state.ent.name.." doesn't know where to buy "..state.action.item)
end
end
dbg.v1(state.ent.name.." can't obtain "..state.action.item)
return true, false
end

View File

@ -11,7 +11,7 @@ people.actions.place = function(state)
end
local distance = vector.distance(state.pos, state.action.pos)
if distance > 5 then
if distance > 5.5 then
dbg.v1(state.ent.name.." too far away to place - distance is "..distance)
return true, false
end
@ -42,15 +42,47 @@ people.actions.place = function(state)
local ii = ItemStack(state.action.item.." 1")
if not inv:contains_item("main", ii) then
dbg.v1(state.ent.name.." doesn't have "..state.action.item.." to place")
return true, false
return true, {"need", item=state.action.item}
end
-- We need a node to build against...
local todir = state.action.againstdir
if not todir then
local trydirs
if state.action.againstdir then
trydirs = {againstdir}
else
trydirs = {{x=0, y=-1, z=0},
{x=0, y=1, z=0},
{x=-1, y=0, z=0},
{x=1, y=0, z=0},
{x=0, y=0, z=-1},
{x=0, y=0, z=1}}
end
for _, trydir in pairs(trydirs) do
local n = minetest.get_node(vector.add(state.action.pos, trydir))
if n.name ~= "air" then
todir = trydir
break
end
end
if not todir then
dbg.v1(state.ent.name.." can't place - nothing to build against")
return true, false
end
end
local pointed_thing = {type="node",
under={x=state.action.pos.x,y=state.action.pos.y-1,z=state.action.pos.z},
under=vector.add(state.action.pos, todir),
above=vector.new(state.action.pos)}
onplace = minetest.registered_items[state.action.item].on_place
if not onplace then onplace = minetest.item_place end
rstack = onplace(ii, people.get_fake_player(state.ent), pointed_thing)
-- Setting the param2 here, but nil is acceptable if it wasn't given as a
-- parameter to the action (normally it shouldn't need to be - maybe just
-- for stairs?)
rstack = onplace(ii, people.get_fake_player(state.ent), pointed_thing, state.action.param2)
inv:remove_item("main", ItemStack(state.action.item.." 1"))
dbg.v1(state.ent.name.." placed "..state.action.item.." at "..minetest.pos_to_string(state.action.pos))
return true, true

View File

@ -4,32 +4,43 @@ if moddebug then dbg=moddebug.dbg("people") else dbg={v1=function() end,v2=funct
local function move_items(srcinv, destinv, itemspec)
local itemname
local count = 0
if string.sub(itemspec, 1, 1) == "*" then
itemname = string.sub(itemspec, 2)
itemspec = itemname .. " 99"
while true do
local stack = srcinv:remove_item("main", ItemStack(itemspec))
if stack:get_count() == 0 then break end
count = count + stack:get_count()
stack = destinv:add_item("main", stack)
count = count - stack:get_count()
if stack:get_count() ~= 0 then
srcinv:add_item("main", stack)
break
end
end
local removespec
local single = true
local keepstack = nil
if type(itemspec) == "string" then
removespec = itemspec
else
local stack = srcinv:remove_item("main", ItemStack(itemspec))
count = count + stack:get_count()
itemname = stack:get_name()
stack = destinv:add_item("main", stack)
count = count - stack:get_count()
srcinv:add_item("main", stack)
removespec = itemspec[1]
if itemspec.keep then
keepstack = srcinv:remove_item("main", ItemStack(removespec.." "..itemspec.keep))
end
end
if not removespec:find(" ") then
removespec = removespec.." 99"
single = false
end
return itemname, count
local count = 0
while true do
local stack = srcinv:remove_item("main", ItemStack(removespec))
dbg.v3("remove_item of '"..removespec.."' removed a stack of "..stack:get_count())
if stack:get_count() == 0 then break end
count = count + stack:get_count()
local leftover = destinv:add_item("main", stack)
count = count - leftover:get_count()
if leftover:get_count() ~= 0 then
srcinv:add_item("main", stack)
break
end
if single then break end
end
if keepstack then
srcinv:add_item("main", keepstack)
end
return removespec, count
end

268
actions/tunnel.lua Normal file
View File

@ -0,0 +1,268 @@
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

View File

@ -11,6 +11,7 @@ local get_person = function(args)
if not args then return nil, nil, nil end
local person_name
person_name, args = string.match(args, "^([^ ]+)(.*)")
if person_name and not people.is_valid_name(person_name) then
@ -174,6 +175,47 @@ subcmd.where = {
end
}
subcmd.attach = {
params = "[<name>]",
desc = "Attach to a person (or remove, with no parameter)",
exec = function(playername, args)
if not minetest.check_player_privs(playername, {server=true}) then
return false, "Only admins can attach to people"
end
local function removecur(playername)
if not people.attach_by_player[playername] then return end
local player = minetest.get_player_by_name(playername)
player:set_detach()
end
local person, person_name
person_name, person, args = get_person(args)
if not person then
if args then
return false, "No such person as "..person_name
end
if not people.attach_by_player[playername] then
return false, "Not attached to a person"
end
removecur(playername)
local n = people.attach_by_player[playername].name
people.attach_by_player[playername] = nil
return true, "Detached from "..n
end
removecur(playername)
people.attach_by_player[playername] = {name=person_name}
local player = minetest.get_player_by_name(playername)
local ent = person.entity
if not ent then
return false, nil, true
end
player:set_attach(ent.object, "", {x=0,y=0,z=0}, {x=0,y=0,z=0})
return true, "Attached to "..person_name
end
}
subcmd.track = {
params = "[<name>]",
desc = "Track a person on the HUD (or remove, with no parameter)",

View File

@ -25,6 +25,9 @@ minetest.register_on_player_receive_fields(function(sender, formname, fields)
"list[current_player;main;0,5;8,4;]"
minetest.show_formspec(playername, "people:invform", formspec)
elseif fields.skin then
minetest.show_formspec(playername, "people:skinform", skins.formspec.main(playername))
elseif fields.program then
if string.sub(entity.fcode, 1, 1) ~= "@" then
entity.code = fields.code
@ -33,7 +36,10 @@ minetest.register_on_player_receive_fields(function(sender, formname, fields)
end
-- Some things get reset when programming...
entity.gather = {items={}, nodes={}, topnodes={}, plant=nil}
entity.gather = {items={}, nodes={}, topnodes={}, plant=nil,
animal = nil, -- name of animal to gather from
animal_wield = nil, -- when animal set, the item to wield
}
entity.action = nil
entity.wait = 0
@ -64,6 +70,27 @@ minetest.register_on_player_receive_fields(function(sender, formname, fields)
-- Just a placeholder - doesn't need any handling currently
elseif formname == "people:skinform" then
local playername = sender:get_player_name()
local entity = lastformbyplayer[playername]
if not entity then return end
if playername ~= entity.owner then return end
if fields.main then
people.show_form(entity, playername)
return
end
for field, _ in pairs(fields) do
if string.sub(field,0,string.len("skins_set_")) == "skins_set_" then
local req = skins.list[tonumber(string.sub(field,string.len("skins_set_")+1))]
entity.props.textures = {req..".png"}
entity:update_props()
end
if string.sub(field,0,string.len("skins_page_")) == "skins_page_" then
skins.pages[playername] = tonumber(string.sub(field,string.len("skins_page_")+1))
minetest.show_formspec(playername, "people:skinform", skins.formspec.main(playername))
end
end
end
end)
@ -105,8 +132,9 @@ people.show_form = function(entity, playername)
presetnum = 1
entity.fcode = "@Example"
end
formspec = formspec.."button[2.5,6;2.5,1;program;Program]"..
"button[5.5,6;2.5,1;inventory;Inventory]"..
formspec = formspec.."button[1.5,5;2.5,1;program;Program]"..
"button[4,5;2.5,1;inventory;Inventory]"..
"button[6.5,5;2.5,1;skin;Skin]"..
"label[0,7;Preset]"..
"dropdown[2,7;3;preset;"..prelist..";"..presetnum + 1 .."]"
else

View File

@ -4,6 +4,7 @@ if moddebug then dbg=moddebug.dbg("people") else dbg={v1=function() end,v2=funct
people = {}
people.hud_show_by_player = {}
people.attach_by_player = {}
local people_modpath = minetest.get_modpath("people")
dofile(people_modpath .. "/commands.lua")
@ -27,10 +28,15 @@ dofile(people_modpath .. "/actions/place.lua")
dofile(people_modpath .. "/actions/gather.lua")
dofile(people_modpath .. "/actions/stash_and_retrieve.lua")
dofile(people_modpath .. "/actions/buy_and_sell.lua")
dofile(people_modpath .. "/actions/obtain.lua")
dofile(people_modpath .. "/actions/craft.lua")
dofile(people_modpath .. "/actions/attackplayer.lua")
dofile(people_modpath .. "/actions/pickup.lua")
dofile(people_modpath .. "/actions/rightclicknode.lua")
dofile(people_modpath .. "/actions/harvest.lua")
dofile(people_modpath .. "/actions/tunnel.lua")
dofile(people_modpath .. "/actions/building.lua")
dofile(people_modpath .. "/actions/claimplot.lua")
people.presets = {}
for p, priv in pairs({FollowOwner=false,
@ -38,6 +44,7 @@ for p, priv in pairs({FollowOwner=false,
RouteWalker=false,
FarmHand=true,
CowTest=true,
Miner=true,
TreeFarmer=true}) do
local file = io.open(minetest.get_modpath("people").."/presets/"..p..".lua", "r")
local code
@ -554,23 +561,28 @@ minetest.register_entity("people:person" ,{
if action_done then
local lastaction = state.action
if state.action.prevaction then
dbg.v3(state.ent.name.." completed action - setting previous action")
dbg.v3(state.ent.name.." completed action - setting prevaction")
self.action = state.action.prevaction
else
if(action_success) then
if action_success == true then
dbg.v3(state.ent.name.." completed action")
if state.action.successaction then
dbg.v3(state.ent.name.." completed action - setting successaction")
self.action = state.action.successaction
else
self.action = nil
end
else
dbg.v3(state.ent.name.." failed action")
end
if not action_success then
local event = {type="actfail"}
dbg.v3(state.ent.name.." failed action: "..minetest.serialize(action_success))
self.action = nil
local event = {type="actfail", failedaction=lastaction, failcode=action_success}
local err = people.exec_event(self, event)
if err then dbg.v1("Lua error "..err) end
end
self.action = nil
end
end
@ -716,6 +728,7 @@ people.exec_event = function(ent, event)
local env = ent.env
env.action = ent.action
env.name = ent.name
env.gather = ent.gather
env.mem = ent.memory
env.carrying = function(item)
@ -733,6 +746,7 @@ people.exec_event = function(ent, event)
local inv = ent.inventory
return currency.count_money(inv, "main")
end
env.vector = vector
env.event = event

View File

@ -132,16 +132,17 @@ if event.type == "program" then
{"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",
"*farming:wheat", "*farming_plus:carrot_item", "*farming:seed_wheat",
"*farming_plus:rhubarb_item",
"*farming_plus:carrot_seed", "*farming:seed_cotton",
"*farming_plus:tomato_seed", "*farming_plus:tomato_item",
"*farming_plus:rhubarb_seed", "*farming_plus:orange_seed",
"*farming_plus:potato_seed", "*farming_plus:potato_item",
"*farming_plus:orange_item",
"*farming_plus:strawberry_item", "*farming_plus:strawberry_seed",
"*farming_plus:cocoa", "default:tree"}},
{"stash", dest="default:chest", items={"farming:weed", "farming:cotton",
"farming:wheat", "farming_plus:carrot_item", "farming:seed_wheat",
"farming_plus:rhubarb_item",
"farming_plus:carrot_seed", "farming:seed_cotton",
"farming_plus:tomato_seed", "farming_plus:tomato_item",
"farming_plus:rhubarb_seed", "farming_plus:orange_seed",
"farming_plus:potato_seed", "farming_plus:potato_item",
"farming_plus:orange_item",
"farming:pumpkin",
"farming_plus:strawberry_item", "farming_plus:strawberry_seed",
"farming_plus:cocoa", "default:tree"}},
{"go", pos={x=174, y=11.5, z=320}},
@ -165,14 +166,13 @@ if event.type == "program" then
-- Chuck all harvested stuff in the recycle bin. It will end up
-- at the factory.
{"go", name="path_Jeiffel Tower"},
{"go", name="Jeiffel Recycle Bin"},
{"wait", time=5},
{"go", pos={x=-221,y=1.5,z=-187}},
{"stash", dest="default:chest",
items={"*default:tree",
"*default:leaves",
"*default:apple",
"*default:sapling"}},
items={"default:tree",
"default:leaves",
"default:apple",
"default:sapling"}},
-- Turn off gathering now
{"gather"},
@ -181,19 +181,18 @@ if event.type == "program" then
-- ********************
-- Head f0r the cactus farm, but get some flowers on the way
--
{"go", name="path_Jeiffel Station"},
{"gather", nodes={"flowers:geranium",
"flowers:rose",
"flowers:dandelion_white",
"flowers:dandelion_yellow",
"flowers:viola",
"flowers:tulip"}},
{"go", name="path_Cactus Approach"},
{"go", name="Near Cactusville"},
{"gather"},
-- ********************
-- Cactus Farm
{"go", name="path_Cactus Farm"},
{"go", name="Cactus Farm"},
{"wait", time=10},
{"gather", topnodes={"default:cactus"}},
{"go", pos={x=-34, y=11.5, z=-462}},
@ -203,14 +202,14 @@ if event.type == "program" then
{"go", pos={x=-33, y=11.5, z=-465}},
{"go", pos={x=-34, y=11.5, z=-462}},
{"gather"},
{"go", name="path_Cactusville Square"},
{"stash", items={"*default:cactus",
"*flowers:geranium",
"*flowers:rose",
"*flowers:dandelion_white",
"*flowers:dandelion_yellow",
"*flowers:viola",
"*flowers:tulip",
{"go", name="Cactusville Square"},
{"stash", items={"default:cactus",
"flowers:geranium",
"flowers:rose",
"flowers:dandelion_white",
"flowers:dandelion_yellow",
"flowers:viola",
"flowers:tulip",
}, dest="default:chest"},
@ -223,15 +222,13 @@ if event.type == "program" then
{"go", pos={x=153, y=4.5, z=203}},
{"go", pos={x=156, y=4.5, z=202}},
{"go", pos={x=156, y=4.5, z=199}},
{"stash", items={"*default:papyrus"}, dest="default:chest"},
{"stash", items={"default:papyrus"}, dest="default:chest"},
{"go", pos={x=156, y=4.5, z=202}},
{"go", pos={x=153, y=4.5, z=203}},
{"go", name="path_Industrial Estate"},
-- *****************
-- A bit of random wandering f0r test purposes
{"go", name="path_Building Supplies Shop"},
{"wait", time=10},
{"go", name="path_Mia's Shop"},
{"wait", time=10},
{"go", name="path_Adventure Shop"},

231
presets/Miner.lua Normal file
View File

@ -0,0 +1,231 @@
local MODE_selling = 1 -- Sell stuff
local MODE_inventory = 2 -- Manage our inventory
local MODE_mining = 3 -- Do mining
local MODE_exitmine = 4 -- Get out of the mine
local MODE_minebuilding = 5 -- Build the mine building
local MODE_homebuilding = 6 -- Build a home
local MODE_panic = 7 -- Don't know what to do
local inventory_actions = {
-- All these have a prevaction on to prevent them raising errors.
-- We still (probably) want to try and carry on mining if we're
-- missing some materials - maybe we'll be able to get them later.
{"obtain", item="stairs:stair_cobble", num=40, prevaction={"wait", time=1}},
{"obtain", item="default:stone", num=40, prevaction={"wait", time=1}},
{"obtain", item="default:wood", num=40, prevaction={"wait", time=1}},
{"obtain", item="stairs:slab_wood", num=20, prevaction={"wait", time=1}},
{"obtain", item="default:stick", num=40, prevaction={"wait", time=1}},
{"obtain", item="default:torch", num=20, prevaction={"wait", time=1}},
{"go", name="Jeiffel Recycle Bin", successaction={"stash",
dest="default:chest",
items={{"default:dirt", keep=50},
{"default:cobble", keep=50},
"default:snow",
"default:gravel"
}
}},
{"go", name="Miner Willy's"}
}
if event.type == "program" then
mem.mode = MODE_selling
mem.cur = 1
mem.lastxmode = MODE_mining
mem.needed = {}
elseif event.type == "actfail" then
if mem.mode == MODE_inventory or mem.mode == MODE_selling or
mem.mode == MODE_minebuilding or mem.mode == MODE_homebuilding then
if type(event.failcode) == "table" and event.failcode[1] == "need" then
table.insert(mem.needed, event.failcode.item)
end
action = {"wait", time=5}
mem.mode = MODE_selling
mem.cur = 1
elseif mem.mode == MODE_mining then
mem.mode = MODE_exitmine
mem.cur = #mem.exitmine
elseif mem.mode == MODE_exitmine then
-- If something goes wrong when exiting the mine, just keep
-- trying
action = {"wait", time=5}
mem.cur = mem.cur + 1
else
mem.mode = MODE_panic
end
elseif event.type == "act" then
if mem.mode == MODE_selling then
if carrying("default:iron_lump") > 0 then
action = {"go", name="Mineral Shop", successaction={"sell", item="default:iron_lump"}}
return
end
-- TODO - need to sell excess coal here, but the sell action currently
-- doesn't allow an amount to be specified, so we'd sell it all!
mem.mode = MODE_inventory
mem.cur = 1
action = {"wait", time=1}
elseif mem.mode == MODE_inventory then
if #mem.needed > 0 then
action={"obtain", item=mem.needed[1], num=1, prevaction={"wait", time=1}}
table.remove(mem.needed, 1)
return
end
action = inventory_actions[mem.cur]
mem.cur = mem.cur + 1
if mem.cur > #inventory_actions then
if mem.lastxmode == MODE_minebuilding then
mem.mode = MODE_mining
elseif mem.lastxmode == MODE_mining then
mem.mode = MODE_homebuilding
elseif mem.lastxmode == MODE_homebuilding then
mem.mode = MODE_mining
end
mem.lastxmode = mem.mode
mem.cur = 1
end
elseif mem.mode == MODE_mining then
if mem.cur == 1 then
action = {"go", name="Miner Willy's",
successaction={"gather", items={"default:torch"}}}
mem.exitmine = {}
mem.cur = 2
elseif mem.cur == 2 then
-- Select some materials to fancy up the tunnel, if we're carrying
-- them. If not, we'll do it without them and they'll get done
-- another time.
local steps = nil
local wall_and_floor = nil
local support_c = nil
local support_s = nil
local lighting = nil
if carrying("stairs:stair_cobble") > 3 then
steps = "stairs:stair_cobble"
if carrying("default:wood") > 3 then
support_c = "default:wood"
end
if carrying("stairs:slab_wood") > 3 then
support_s = "stairs:slab_wood"
end
if carrying("default:stone") > 10 then
wall_and_floor = "default:stone"
end
end
if carrying("default:torch") > 1 then
lighting = "default:torch"
end
action = {"tunnel", distance=40, dir={x=0,y=-1,z=-1}, steps=steps,
lighting=lighting, support_ceiling=support_c,
support_side=support_s,
support_side_facein=true,
wall=wall_and_floor, floor=wall_and_floor}
action.successaction= {"tunnel", distance=8, dir={x=0,y=0,z=-1},
lighting=lighting, floor=wall_and_floor,
ceiling=wall_and_floor}
action.successaction.successaction={"go", rel={x=1,y=0,z=2}}
table.insert(mem.exitmine, {"go", name="Miner Willy's"})
mem.cur = 3
elseif mem.cur == 3 then
action = {"tunnel", distance=20, dir={x=1,y=-1,z=0}, steps="stairs:stair_cobble",
lighting="default:torch", wall="default:stone", floor="default:stone"}
action.successaction=
{"tunnel", distance=8, dir={x=1,y=0,z=0}, lighting="default:torch", successaction=
{"go", rel={x=-1,y=0,z=1}} }
table.insert(mem.exitmine, {"go", pos=vector.subtract(pos, {x=1,y=0,z=0})})
mem.cur = 4
elseif mem.cur == 4 then
mem.mode = MODE_exitmine
mem.cur = #mem.exitmine
action = {"wait", time=1}
end
elseif mem.mode == MODE_exitmine then
if mem.cur == 0 or not mem.exitmine then
mem.exitmine = nil
mem.mode = MODE_selling
mem.cur = 1
action = {"gather"}
else
action = mem.exitmine[mem.cur]
mem.cur = mem.cur - 1
end
elseif mem.mode == MODE_minebuilding then
if mem.cur == 1 then
action = {"go", name="Miner Willy's"}
mem.cur = 2
elseif mem.cur == 2 then
action = {"building", dir={x=0,y=-1,z=-1},
width=5, depth=7, height=5,
wall="default:stone",
ceiling="default:wood",
outside_floor={"default:dirt", "default:dirt_with_grass"},
door="doors:door_wood"
}
mem.cur = 3
else
mem.mode = MODE_selling
mem.cur = 1
action = {"wait", time=5}
end
elseif mem.mode == MODE_homebuilding then
local home_jctname = "people_"..name.."_home"
if mem.cur == 1 then
-- Attempt to claim our plot. If we already have it, this will fail
-- and we'll just do the wait.
action = {"claimplot", name=home_jctname, prevaction={"wait", time=1}}
mem.cur = 2
elseif mem.cur == 2 then
action = {"go", name=home_jctname, faceexit="@build"}
mem.cur = 3
elseif mem.cur == 3 then
-- We'd better be facing the right way!!
local bdir = vector.round(vector.from_speed_yaw(1, yaw))
action = {"building", dir=bdir,
width=5, depth=5, height=4,
wall="default:stone",
ceiling="default:wood",
outside_floor={"default:dirt", "default:dirt_with_grass"},
door="doors:door_wood"
}
mem.cur = 4
else
mem.mode = MODE_selling
mem.cur = 1
action = {"wait", time=5}
end
else
action = {"wait", time=600}
end
end

View File

@ -25,10 +25,10 @@ if event.type == "program" then
{"wait", time=5},
{"go", pos={x=-221,y=1.5,z=-187}},
{"stash", dest="default:chest",
items={"*default:tree",
"*default:leaves",
"*default:apple",
"*default:sapling"}}
items={"default:tree",
"default:leaves",
"default:apple",
"default:sapling"}}
}