Compare commits

...

5 Commits

Author SHA1 Message Date
Ciaran Gultnieks eda0d51861 More wip 2015-05-24 10:25:51 +01:00
Ciaran Gultnieks a5c2ff2ee1 A ton of stuff 2015-05-19 23:29:08 +01:00
Ciaran Gultnieks fac95b47af Update presets 2015-05-10 08:50:45 +01:00
Ciaran Gultnieks e21318ab9d Support for cocoa sapling planting 2015-05-10 08:50:24 +01:00
Ciaran Gultnieks 8dff43a47c Complete migration of footpath management out of this mod 2015-05-10 08:50:09 +01:00
18 changed files with 1791 additions and 249 deletions

200
README.md
View File

@ -104,7 +104,13 @@ functionality. When editing, select one of the presets from the dropdown and tha
preset will be used. If you subsequently select "Custom", the code from the
previously selected preset will be loaded as a base for further editing.
A brief description of these:
The presets can be reloaded from disk while the server is running, using the
chat command `/people reload_presets`. The new versions will start being used
immediately by any active person set to that preset (but obviously where a
preset has been used as a base for a "Custom" setting, that will not be
affected).
A brief description of some of the presets currently available:
####FollowOwner
When programmed, follows the programmer until they log off
@ -120,7 +126,15 @@ Responds to simple commands. Some examples:
Look at the code for more things it can do.
####RouteWalker
Follows a preset route (hard-coded, so you would want to edit it) indefinitely.
Follows a fixed route indefinitely. After programming, the person has no
route defined. To give it the first point on its route, you `/tell` it the
command "here". The place you're standing when you say that becomes the
first point on its route.
You can then follow that with any additional number of "then here" commands,
which add further points to the route.
The route is followed from start to finish, and then back the other way.
## Events
@ -134,7 +148,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 +207,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 +248,22 @@ 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} - this specifies a direction to the
node against which the placing will be done. If it's not specified, a
direction will automatically be figured out, if possible. Normally it's
fine to let this be figured out - you would specify it if it's essential
the node is placed against a specific one, for example, when placing a
torch against a wall.
#### 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 +275,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 +306,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 +381,70 @@ 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.
* `sdist=n` - special things (lighting, supports) are placed in
a repeating 8 node pattern that begins where the
tunnelling starts. This can be used to shift the
start point of that. Generally 1-7 would be
specified here, if anything.
* 'openside={}` - if present, can have either left or right=true,
or both. The end (last three nodes) of the tunnel
will be left open on that/those sides.
* 'endface={} - as for openside, but after the tunneling is
complete, the person will fininsh positioned and
facing correctly to being a tunnel through that
opening. If both are specified, one is picked at
random.
#### 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
@ -384,9 +507,6 @@ Track the given person (location and distance away) on the HUD.
Use without a parameter to switch it off.
### /people footpath_update
Update the footpath network.
## Autonomy and Activation
People are 'autonomous' - i.e. they will continue to act even when no player
@ -416,35 +536,19 @@ Ultimately it won't use this 'feature' at all.
## Footpaths and Navigation
People (in particular via the 'go' action) have an in-built understanding of
footpaths, which are laid out across the map by players, usually using cobble
and cobble steps (but wood can also be used, e.g. for bridges). In addition,
special areas (via the areas mod) are used to 'mark up' routes along these
footpaths. This provides people with the ability to do sensible long-range
navigation as a player would.
Path junctions are marked up by creating a 1x1x1 area above the junction
node. They are always named `path_xxxxx`, where `xxxxx` is the unique name for
that junction - e.g. `path_My House`.
footpaths, which are managed via the footpath mod. This provides people with
the ability to do sensible long-range navigation as a player would.
When following a footpath, preference will always be given to the direction
currently being travelled in. The means that, for example, a cobblestone
footpath can cross a larger area 'paved' with cobblestone (or other footpath
materials), so long as the crossing is in a straight line.
Additionally, above each node leading away from the junction, a 1x1x1 area
with the name `to_xxxxx` is used. In this case, `xxxxx` is the name of the
path node this direction leads to - e.g. `to_Your House`.
Changes to the footpath network only take effect when the `/people footpath_update`
command is used. At this point, if anything is wrong with the network, this
will be indicated, and the previous network will remain in effect. The current
network is saved across server restarts.
If you tell a person to go to a named place (e.g. {"go", name="My House"}) they
will first look for `path_My House`, and then also find the nearest path
junction to where they are now. They will then navigate along the path network
from one to the other.
will first look for a footpath junction with that name, and then also find the
nearest junction to where they are now. They will then navigate along the path
network from one to the other.
If there is no `path_My House` they will also look for just `My House`, and if
that's found, head directly for the centre of that area.
If there is no footpath junction with that name, they'll also look for an area
(areas mod) and head directly for the centre of that area.

225
actions/building.lua Normal file
View File

@ -0,0 +1,225 @@
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 < 3
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 < 3
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
local hh = state.action.height
if not state.action.ceiling then
hh = hh - 1
end
for y = 0, hh do
local placeitem = state.action.wall
if y == hh then
placeitem = state.action.ceiling
end
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=placeitem,
successaction=state.action,
againstdir=todir}
return false
elseif y >= starty and dignode.name == "air" then
state.action = {"place", pos=thispos, item=placeitem,
successaction=state.action}
return false
elseif (y >= starty and dignode.name ~= placeitem)
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

@ -12,8 +12,18 @@ local ANTP = -0.5
-- This is all very messy and needs rationalising!!
local PNTP = 0.5
-- Maximum distance from a destination at which we will attempt to use
-- pathfinding (e.g. after a collision)
local MAX_PATH_DIST = 48
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
@ -27,20 +37,20 @@ people.actions.go = function(state)
-- Look for a footpath node with the right name
if footpath then
local pathdestpos = footpath.get_nodepos("path_"..state.action.name)
local pathdestpos = footpath.get_nodepos(state.action.name)
if pathdestpos then
dbg.v2(state.ent.name.." selected path destination path_"..state.action.name)
dbg.v2(state.ent.name.." selected path destination "..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 pathstartnode = footpath.nearest_node(state.pos)
-- there. (Note, the nearest junction could be the destination,
-- and that's ok!)
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)
state.action.pos.y = state.action.pos.y + ANTP
state.action.footpathdest = "path_"..state.action.name
state.action.footpathdest = state.action.name
return false
end
end
@ -64,10 +74,11 @@ 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
-- path handling - only comes here when we've arrived at a new path
-- square (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 +88,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
@ -88,7 +129,7 @@ people.actions.go = function(state)
-- We're at a junction
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 = footpath.get_route(curpathnode.name, "path_"..state.action.name)
local nextpathdest = footpath.get_route(curpathnode.name, state.action.name)
if not nextpathdest then
dbg.v1(state.ent.name.." can't find route")
return true, false
@ -129,7 +170,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 +184,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 +202,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 +235,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)
@ -260,7 +305,7 @@ people.actions.go = function(state)
-- We've hit something...
-- Apply some damage for bashing into something. Apart from being
-- the right thing to do, it will also kill of things that get
-- the right thing to do, it will also kill off things that get
-- stuck in a wall. ;)
state.damage = 0.4
end
@ -271,75 +316,82 @@ 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
if unwholesome then
if distance < 32 then
if distance <= MAX_PATH_DIST 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.intermediate = nil
state.action = {"wait", time=30, prevaction=state.action}
return false
end
local spos = vector.new(state.pos)
@ -361,9 +413,7 @@ people.actions.go = function(state)
else
if not state.action.intermediate or #state.action.intermediate > 10 then
state.action.intermediate = {}
end
state.action.intermediate = {}
-- Start following the path from the rounded position, in case
-- we're off centre and will collide immediately...

69
actions/obtain.lua Normal file
View File

@ -0,0 +1,69 @@
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" or
state.action.item == "default:stonebrick" or
state.action.item == "default:cobble" 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,61 @@ 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, {"need", item=state.action.item}
end
-- We need a node to build against...
local trydirs, todir
if state.action.againstdir then
trydirs = {state.action.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
local reasons = ""
for _, trydir in pairs(trydirs) do
local n = minetest.get_node(vector.add(state.action.pos, trydir))
-- Certainly can't place against air (but maybe that's covered
-- by buildable_to? Check TODO
if n.name ~= "air" then
-- Don't try and place a slab against a slab of the same kind,
-- because they can end up combinining
if n.name:sub(1, 11) == "stairs:slab" and
n.name == state.action.item then
reasons = reasons.."/similar slab at "..minetest.pos_to_string(trydir)
else
local def = minetest.registered_nodes[n.name]
if def and not def.buildable_to then
todir = trydir
break
else
reasons = reasons.."/unknown or buildable_to node at "..minetest.pos_to_string(trydir)
end
end
else
reasons = reasons.."/air at "..minetest.pos_to_string(trydir)
end
end
if not todir then
dbg.v1(state.ent.name.." can't place - nothing to build against : "..reasons)
return true, false
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

345
actions/tunnel.lua Normal file
View File

@ -0,0 +1,345 @@
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 math.abs(state.action.dir.y) > 1 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
if state.action.endface then
local endoff = {y=0, z=-1}
local endface = {y=0, z=-1}
if state.action.endface.left and state.action.endface.right then
if math.random() < 0.5 then
endoff.x = -1
else
endoff.x = 1
end
elseif state.action.endface.left then
endoff.x = -1
elseif state.action.endface.right then
endoff.x = 1
end
endface.x = 2 * endoff.x
endoff = rotate(endoff, state.action.dir)
endface = rotate(endface, state.action.dir)
state.action.endface = nil
state.action = {"go", rel=endoff, successaction={"face",
pos=vector.add(state.pos, endface),
successaction=state.action}}
return false
end
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 height = 2
if state.action.dir.y ~= 0 then
if state.action.steps then
height = 4
else
height = 3
end
end
-- Work out if either or both sides of the tunnel should be open at
-- this point...
local leftopen = false
local rightopen = false
if state.action.distance < 4 and state.action.openside then
if state.action.openside.left then leftopen = true end
if state.action.openside.right then rightopen = true 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 sdist = 0
if state.action.sdist then
sdist = state.action.sdist
end
local special_sup = (state.action.startdist - state.action.distance - sdist + 2) % 8 == 7
local special_light = (state.action.startdist - state.action.distance - sdist) % 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.
-- On the other hand, there are things like falling gravel and
-- sand to take into account.
for y = yfrom, yto, ydir do
for x = -1, 1 do
local thisoff = rotate({x=x, y=y, z=0}, state.action.dir)
local thispos = vector.add(digpos, thisoff)
local dignode = minetest.get_node(thispos)
local openside = false
if (x == -1 and leftopen) or
(x == 1 and rightopen) then
openside = true
end
-- 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 not openside 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 not openside 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 not openside and state.action.support_side
and special_sup then
-- As with lighting, we don't actually put in the side supports
-- now, because they're placed against the wall which we might
-- not have done yet.
if dignode.name ~= "air" and 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 = rotate({x=x, y=-1, z=0}, state.action.dir)
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 and not (leftopen and rightopen) then
local sx = -2
local fx = 2
if leftopen then
sx = fx
elseif rightopen then
fx = sx
end
for y = 0, height + 1 do
for x = sx, fx, 4 do
thisoff = rotate({x=x, y=y, z=0}, state.action.dir)
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 and
not (leftopen and rightopen) and
people.is_carrying(state.ent, state.action.lighting) > 0 then
local sx = -1
local fx = 1
if leftopen then
sx = fx
elseif rightopen then
fx = sx
end
for x = sx, fx, 2 do
local thisoff = rotate({x=x, y=2, z=0}, state.action.dir)
local thispos = vector.add(digpos, thisoff)
local dignode = minetest.get_node(thispos)
if dignode.name ~= state.action.lighting then
local againstdir = rotate({x=x, y=0, z=0}, state.action.dir)
state.action = {"place", pos=thispos, item=state.action.lighting, successaction=state.action,
againstdir=againstdir}
return false
end
end
end
if state.action.support_side and special_sup and
not (leftopen and rightopen) and
people.is_carrying(state.ent, state.action.support_side) > 0 then
local sx = -1
local fx = 1
if leftopen then
sx = fx
elseif rightopen then
fx = sx
end
local sy = 0
if state.action.steps then
sy = 1
end
for y = sy, height do
for x = sx, fx, 2 do
thisoff = rotate({x=x, y=y, z=0}, state.action.dir)
local thispos = vector.add(digpos, thisoff)
local dignode = minetest.get_node(thispos)
if dignode.name ~= state.action.support_side 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 * 3
elseif facevec.x < 0 then
param2 = 4 * 4
elseif facevec.z > 0 then
param2 = 4 * 1
else
param2 = 4 * 2
end
end
state.action = {"place", pos=thispos,
item=state.action.support_side,
successaction=state.action,
param2=param2}
return false
end
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
for x = -1, 1 do
local thisoff = rotate({x=x, y=height+1, z=0}, state.action.dir)
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}
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
@ -55,21 +56,17 @@ subcmd.help = {
end
}
subcmd.footpath_update = {
params = "",
desc = "Update footpath node graph",
subcmd.reload_presets = {
params = "none",
desc = "Reload the presets from disk",
exec = function(playername, args)
if not minetest.check_player_privs(playername, {server=true}) then
return false, "Only admins can update the footpath node graph"
return false, "Only admins can reload presets"
end
response = footpath.make_pathnodes()
if response then
return false, response
end
return true, "Footpath node graph updated successfully"
people.load_presets()
return true, "Reloaded presets"
end
}
@ -192,6 +189,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=1,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,28 +28,36 @@ 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,
SimpleCommands=false,
RouteWalker=false,
FarmHand=true,
CowTest=true,
TreeFarmer=true}) do
local file = io.open(minetest.get_modpath("people").."/presets/"..p..".lua", "r")
local code
if file then
code = file:read("*all")
else
code = "MISSING"
people.load_presets = function()
people.presets = {}
for p, priv in pairs({FollowOwner=false,
SimpleCommands=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
if file then
code = file:read("*all")
else
code = "MISSING"
end
table.insert(people.presets, {code=code, name=p, priv=priv})
end
table.insert(people.presets, {code=code, name=p, priv=priv})
end
people.load_presets()
people.is_valid_name = function(name)
if not name then return false end
@ -425,6 +434,8 @@ minetest.register_entity("people:person" ,{
if self.gather.plant == "default:sapling" then
soilitems = {"default:dirt", "default:dirt_with_grass"}
elseif self.gather.plant == "farming_plus:cocoa_sapling" then
soilitems = {"default:desert_sand", "default:sand"}
else
soilitems = {"farming:soil_wet"}
end
@ -552,23 +563,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
@ -697,6 +713,18 @@ local code_prohibited = function(code)
end
end
people.is_carrying = function(ent, item)
local inv = ent.inventory
local count = 0
for i, stack in ipairs(inv:get_list("main")) do
local nn = stack:get_name()
if nn == item then
count = count + stack:get_count()
end
end
return count
end
people.exec_event = function(ent, event)
dbg.v3(ent.name.." exec event "..minetest.serialize(event))
@ -714,23 +742,17 @@ 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)
local inv = ent.inventory
local count = 0
for i, stack in ipairs(inv:get_list("main")) do
local nn = stack:get_name()
if nn == item then
count = count + stack:get_count()
end
end
return count
return people.is_carrying(ent, item)
end
env.has_money = function()
local inv = ent.inventory
return currency.count_money(inv, "main")
end
env.vector = vector
env.event = event

View File

@ -3,7 +3,7 @@ if event.type == "program" then
mem.cur = 0
mem.actions = {
{"go", name="cowfarm"},
{"go", name="path_cowfarm"},
{"wait", time=10},
{"go", pos={x=39,y=11.5,z=189}},
{"wait", time=10},
@ -18,7 +18,7 @@ if event.type == "program" then
{"go", pos={x=39,y=11.5,z=189}},
{"gather"},
{"wait", time=10},
{"go", name="cowfarm"},
{"go", name="path_cowfarm"},
}

View File

@ -4,9 +4,22 @@ if event.type == "program" then
mem.failed = false
mem.actions = {
-- *****************
-- Cocoa Plantation
{"go", name="Cocoa Plantation"},
{"wait", time=5},
{"go", pos={x=121,y=18.5,z=425}},
{"gather", nodes={"default:tree", "farming_plus:cocoa_leaves"},
items={"farming_plus:cocoa", "farming_plus:cocoa_sapling"},
plant="farming_plus:cocoa_sapling"},
{"go", pos={x=126,y=18.5,z=425}},
{"go", pos={x=121,y=18.5,z=425}},
{"gather"},
{"go", name="Cocoa Plantation"},
-- *****************
-- Ciaran's Farm
{"go", name="Ciaran's Farm"},
{"go", name="path_Ciaran's Farm"},
{"wait", time=10},
{"go", pos={x=167,y=11.5,z=316}},
@ -23,7 +36,7 @@ if event.type == "program" then
"farming_plus:rhubarb_seed 20",
"farming:pumpkin_seed 20",
"farming_plus:strawberry_seed 20"}},
{"go", name="Ciaran's Farm"},
{"go", name="path_Ciaran's Farm"},
-- Inside gate
{"go", pos={x=174, y=11.5, z=320}},
@ -98,7 +111,7 @@ if event.type == "program" then
-- Finished. Save seed stock...
{"go", pos={x=174, y=11.5, z=320}},
{"go", name="Ciaran's Farm"},
{"go", name="path_Ciaran's Farm"},
{"wait", time=10},
{"go", pos={x=167,y=11.5,z=316}},
@ -115,25 +128,27 @@ if event.type == "program" then
-- Load up items...
{"go", name="Ciaran's Farm"},
{"go", name="path_Ciaran's Farm"},
{"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"}},
{"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}},
-- *****************
-- Tree Farm
{"go", name="Jeiffel Tree Farm"},
{"go", name="path_Jeiffel Tree Farm"},
{"wait", time=10},
{"go", pos={x=-173,y=9.5,z=-244}},
@ -151,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="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"},
@ -167,14 +181,13 @@ if event.type == "program" then
-- ********************
-- Head f0r the cactus farm, but get some flowers on the way
--
{"go", name="Jeiffel Station"},
{"gather", nodes={"flowers:geranium",
"flowers:rose",
"flowers:dandelion_white",
"flowers:dandelion_yellow",
"flowers:viola",
"flowers:tulip"}},
{"go", name="Cactus Approach"},
{"go", name="Near Cactusville"},
{"gather"},
-- ********************
@ -190,39 +203,37 @@ if event.type == "program" then
{"go", pos={x=-34, y=11.5, z=-462}},
{"gather"},
{"go", name="Cactusville Square"},
{"stash", items={"*default:cactus",
"*flowers:geranium",
"*flowers:rose",
"*flowers:dandelion_white",
"*flowers:dandelion_yellow",
"*flowers:viola",
"*flowers:tulip",
{"stash", items={"default:cactus",
"flowers:geranium",
"flowers:rose",
"flowers:dandelion_white",
"flowers:dandelion_yellow",
"flowers:viola",
"flowers:tulip",
}, dest="default:chest"},
-- *****************
-- Get some papyrus on the way
{"go", name="Sugar Station Entrance"},
{"go", name="path_Sugar Station Entrance"},
{"gather", topnodes="default:papyrus"},
{"go", name="Industrial Corner"},
{"go", name="path_Industrial Corner"},
{"gather"},
{"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="Industrial Estate"},
{"go", name="path_Industrial Estate"},
-- *****************
-- A bit of random wandering f0r test purposes
{"go", name="Building Supplies Shop"},
{"go", name="path_Mia's Shop"},
{"wait", time=10},
{"go", name="Mia's Shop"},
{"wait", time=10},
{"go", name="Adventure Shop"},
--{"go", name="Lava Falls"},
{"go", name="Jake's House"},
{"go", name="path_Adventure Shop"},
--{"go", name="path_Lava Falls"},
{"go", name="path_Jake's House"},
}

466
presets/Miner.lua Normal file
View File

@ -0,0 +1,466 @@
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
-- Items we sell, and (f0r now?) where to sell them
local sell_items = {
{item="default:iron_lump", shop="Mineral Shop"},
{item="default:copper_lump", shop="Mineral Shop"},
{item="default:gold_lump", shop="Mineral Shop"},
{item="default:mese_crystal", shop="Mineral Shop"},
{item="default:diamond", shop="Mineral Shop"},
-- Want to sell excess default:coal_lump, but the sell action
-- only allows selling all currently - TODO
}
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.
--
-- Note that many of these would be automatically obtained via
-- the 'needed' system. But f0r now, unt1l that's more intelligent
-- we do these basics in order here, because e.g. if a torch is
-- needed, the best thing to do is use wood to make sticks to make
-- the torches.
{"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"
}
}},
}
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 mem.cur > #sell_items then
action = {"wait", time=0.5}
mem.mode = MODE_inventory
mem.cur = 1
return
end
local item = sell_items[mem.cur].item
if carrying(item) > 0 then
action = {"go", name=sell_items[mem.cur].shop,
successaction={"sell", item=item,
prevaction={"wait", time=5}}}
else
action = {"wait", time=0.5}
end
mem.cur = mem.cur + 1
return
elseif mem.mode == MODE_inventory then
if #mem.needed > 0 then
local needitem = mem.needed[1]
local neednum = 1
-- When we need an item, sometimes we'll make a guess that
-- we'll need lots of it, to avoid going backwards and
-- f0rwards...
if needitem == "default:cobble" or
needitem == "default:wood" or
needitem == "default:stonebrick" or
needitem == "default:torch" or
needitem == "default:stick" or
needitem == "stairs:slab_wood" or
needitem == "stairs:stair_cobble" then
neednum = 20
end
action={"obtain", item=needitem, num=neednum,
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_minebuilding
end
mem.lastxmode = mem.mode
mem.cur = 1
end
elseif mem.mode == MODE_mining 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, but in the meantime we'll at least dig the basic
-- tunnel, which should get us the materials/money to get the
-- fancy stuff!
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"
end
if carrying("default:stone") > 10 then
wall_and_floor = "default:stone"
end
if carrying("default:wood") > 3 then
support_c = "default:wood"
end
-- We can set the following two regardless - the tunnel action only
-- places them when carried, and will proceed if they're not.
support_s = "stairs:slab_wood"
lighting = "default:torch"
local mine_jctname = "people_"..name.."_mine"
if mem.cur == 1 then
mem.exitmine = {}
-- Attempt to claim our plot. If we already have it, this will fail
-- and we'll just do the wait.
action = {"claimplot", name=mine_jctname, prevaction={"wait", time=1}}
mem.cur = 2
elseif mem.cur == 2 then
action = {"go", name=mine_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))
-- In case we have a door on the mine, we'll put an exit
-- route inside and then outside. Pathfinding blocks on
-- doors, and if we crash on the way out we might need
-- it.
table.insert(mem.exitmine, {"go", name=mine_jctname})
table.insert(mem.exitmine, {"go", pos=vector.add(pos,
vector.add(bdir, bdir))})
action = {"go", rel=bdir}
action.successaction = {"gather", items={"default:torch"}}
action.successaction.successaction = {"tunnel", distance=40,
dir={x=bdir.x, y=-1, z=bdir.z}, steps=steps,
lighting=lighting, support_ceiling=support_c,
support_side=support_s,
support_side_facein=true,
wall=wall_and_floor, floor=wall_and_floor,
ceiling=wall_and_floor}
action.successaction.successaction.successaction =
{"tunnel", distance=7, sdist=4,
dir={x=bdir.x,y=0,z=bdir.z},
lighting=lighting, support_ceiling=support_c,
support_side=support_s,
support_side_facein=true,
ceiling=wall_and_floor, floor=wall_and_floor,
wall=wall_and_floor,
openside={left=true},
endface={left=true}}
mem.cur = 4
elseif mem.cur == 4 then
-- 4 is go down, and take a left turn to do it again
if pos.y < -200 then
mem.mode = MODE_exitmine
mem.cur = #mem.exitmine
action = {"wait", time=1}
return
end
local bdir = vector.round(vector.from_speed_yaw(1, yaw))
action = {"tunnel", distance=20,
dir={x=bdir.x, y=-1, z=bdir.z}, steps=steps,
lighting=lighting, support_ceiling=support_c,
support_side=support_s,
support_side_facein=true,
wall=wall_and_floor, floor=wall_and_floor,
ceiling=wall_and_floor}
action.successaction =
{"tunnel", distance=7, sdist=4,
dir={x=bdir.x,y=0,z=bdir.z},
lighting=lighting, support_ceiling=support_c,
support_side=support_s,
support_side_facein=true,
ceiling=wall_and_floor, floor=wall_and_floor,
wall=wall_and_floor,
openside={left=true, right=true},
endface={left=true}}
table.insert(mem.exitmine, {"go", pos=vector.subtract(pos, bdir)})
if math.random() < 0.5 then
mem.cur = 5
end
elseif mem.cur == 5 then
-- 5 is go down, and take a right turn to mine on that level
local bdir = vector.round(vector.from_speed_yaw(1, yaw))
action = {"tunnel", distance=20,
dir={x=bdir.x, y=-1, z=bdir.z}, steps=steps,
lighting=lighting, support_ceiling=support_c,
support_side=support_s,
support_side_facein=true,
wall=wall_and_floor, floor=wall_and_floor,
ceiling=wall_and_floor}
action.successaction =
{"tunnel", distance=7, sdist=4,
dir={x=bdir.x,y=0,z=bdir.z},
lighting=lighting, support_ceiling=support_c,
support_side=support_s,
support_side_facein=true,
ceiling=wall_and_floor, floor=wall_and_floor,
wall=wall_and_floor,
openside={left=true, right=true},
endface={right=true}}
table.insert(mem.exitmine, {"go", pos=vector.subtract(pos, bdir)})
mem.cur = 6
elseif mem.cur == 6 then
-- 6 is the very short tunnel to the right of the 'landing'
local bdir = vector.round(vector.from_speed_yaw(1, yaw))
action =
{"tunnel", distance=4, sdist=6,
dir={x=bdir.x,y=0,z=bdir.z},
lighting=lighting, support_ceiling=support_c,
support_side=support_s,
support_side_facein=true,
ceiling=wall_and_floor, floor=wall_and_floor,
wall=wall_and_floor,
openside={right=true},
endface={right=true}}
table.insert(mem.exitmine, {"go", pos=vector.subtract(pos, bdir)})
mem.cur = 7
elseif mem.cur == 7 then
-- 7 is the slightly longer tunnel taking us back in the opposite
-- direction to the stairs
local bdir = vector.round(vector.from_speed_yaw(1, yaw))
action =
{"tunnel", distance=13,
dir={x=bdir.x,y=0,z=bdir.z},
lighting=lighting, support_ceiling=support_c,
support_side=support_s,
support_side_facein=true,
ceiling=wall_and_floor, floor=wall_and_floor,
wall=wall_and_floor,
openside={right=true},
endface={right=true}}
table.insert(mem.exitmine, {"go", pos=vector.subtract(pos, bdir)})
mem.cur = 8
elseif mem.cur == 8 then
-- 8 is the first part of the cross-ways tunnel taking us under the
-- stairs (and also far enough to clear the other stairs when we
-- cross above)
local bdir = vector.round(vector.from_speed_yaw(1, yaw))
action =
{"tunnel", distance=9,
dir={x=bdir.x,y=0,z=bdir.z},
lighting=lighting, support_ceiling=support_c,
support_side=support_s,
support_side_facein=true,
ceiling=wall_and_floor, floor=wall_and_floor,
wall=wall_and_floor}
table.insert(mem.exitmine, {"go", pos=vector.subtract(pos, bdir)})
mem.cur = 9
mem.cur_sections9 = 0
elseif mem.cur == 9 then
-- 9 is all subsequent parts of the cross-ways tunnel, with a choice
-- of three branches at the end of each
mem.cur_sections9 = mem.cur_sections9 + 1
local nxt = 9
if mem.cur_sections9 > 8 then
nxt = 99
end
local ef = nil
local r = math.random()
if r < 0.33 then
ef = {left=true}
nxt = 10
elseif r < 0.66 then
ef = {right=true}
nxt = 10
end
local bdir = vector.round(vector.from_speed_yaw(1, yaw))
action =
{"tunnel", distance=4,
dir={x=bdir.x,y=0,z=bdir.z},
lighting=lighting, support_ceiling=support_c,
support_side=support_s,
support_side_facein=true,
ceiling=wall_and_floor, floor=wall_and_floor,
wall=wall_and_floor,
openside={left=true, right=true},
endface=ef}
table.insert(mem.exitmine, {"go", pos=vector.subtract(pos, bdir)})
mem.cur = nxt
elseif mem.cur == 10 then
-- 10 is a long sideways shoot from the crossways tunnel (either
-- direction)
local bdir = vector.round(vector.from_speed_yaw(1, yaw))
action =
{"tunnel", distance=15,
dir={x=bdir.x,y=0,z=bdir.z},
lighting=lighting, support_ceiling=support_c,
support_side=support_s,
support_side_facein=true,
ceiling=wall_and_floor, floor=wall_and_floor,
wall=wall_and_floor}
table.insert(mem.exitmine, {"go", pos=vector.subtract(pos, bdir)})
mem.cur = 99
elseif mem.cur == 99 then
mem.mode = MODE_exitmine
mem.cur = #mem.exitmine
action = {"wait", time=1}
return
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
local mine_jctname = "people_"..name.."_mine"
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=mine_jctname, prevaction={"wait", time=1}}
mem.cur = 2
elseif mem.cur == 2 then
action = {"go", name=mine_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= {"go", rel=bdir}
action.successaction = {"building", dir=bdir,
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 = 4
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 = {"go", rel=bdir}
action.successaction = {"building", dir=bdir,
width=7, depth=7, height=4,
wall="default:stonebrick",
ceiling="default:wood",
floor="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

@ -3,7 +3,7 @@ if event.type == "program" then
mem.cur = 1
mem.actions = {
{"go", name="Jeiffel Tree Farm"},
{"go", name="path_Jeiffel Tree Farm"},
{"wait", time=10},
{"go", pos={x=-173,y=9.5,z=-244}},
@ -21,14 +21,14 @@ if event.type == "program" then
-- Chuck all harvested stuff in the recycle bin. It will end up
-- at the factory.
{"go", name="Jeiffel Tower"},
{"go", name="path_Jeiffel Tower"},
{"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"}}
}