Compare commits
5 Commits
642cc58348
...
eda0d51861
Author | SHA1 | Date |
---|---|---|
Ciaran Gultnieks | eda0d51861 | |
Ciaran Gultnieks | a5c2ff2ee1 | |
Ciaran Gultnieks | fac95b47af | |
Ciaran Gultnieks | e21318ab9d | |
Ciaran Gultnieks | 8dff43a47c |
200
README.md
200
README.md
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
208
actions/go.lua
208
actions/go.lua
|
@ -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...
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
58
commands.lua
58
commands.lua
|
@ -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)",
|
||||
|
|
34
form.lua
34
form.lua
|
@ -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
|
||||
|
|
86
init.lua
86
init.lua
|
@ -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
|
||||
|
||||
|
|
|
@ -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"},
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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"},
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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"}}
|
||||
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue