More wip
parent
a5c2ff2ee1
commit
eda0d51861
38
README.md
38
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
|
||||
|
||||
|
@ -240,7 +254,12 @@ 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}
|
||||
* 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
|
||||
|
||||
|
@ -387,6 +406,19 @@ Parameters:
|
|||
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
|
||||
|
||||
|
|
|
@ -33,12 +33,12 @@ people.actions.building = function(state)
|
|||
dbg.v1(state.ent.name.." has invalid height for building action")
|
||||
return true, false
|
||||
end
|
||||
if type(state.action.width) ~= "number" or state.action.width < 1
|
||||
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 < 1
|
||||
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
|
||||
|
@ -91,7 +91,18 @@ people.actions.building = function(state)
|
|||
|
||||
if wallpos then
|
||||
|
||||
for y = 0, state.action.height - 1 do
|
||||
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)
|
||||
|
@ -122,14 +133,15 @@ people.actions.building = function(state)
|
|||
todir = {x=-1,y=0,z=0}
|
||||
todir = rotate(todir, state.action.dir)
|
||||
state.action = {"place", pos=thispos,
|
||||
item=state.action.wall,
|
||||
item=placeitem,
|
||||
successaction=state.action,
|
||||
againstdir=todir}
|
||||
return false
|
||||
elseif y >= starty and dignode.name == "air" then
|
||||
state.action = {"place", pos=thispos, item=state.action.wall, successaction=state.action}
|
||||
state.action = {"place", pos=thispos, item=placeitem,
|
||||
successaction=state.action}
|
||||
return false
|
||||
elseif (y >= starty and dignode.name ~= state.action.wall)
|
||||
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
|
||||
|
|
|
@ -12,6 +12,10 @@ 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
|
||||
|
@ -37,10 +41,10 @@ people.actions.go = function(state)
|
|||
if pathdestpos then
|
||||
|
||||
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.
|
||||
-- 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))
|
||||
|
@ -70,8 +74,9 @@ people.actions.go = function(state)
|
|||
|
||||
if not state.action.pos and state.action.footpathdest then
|
||||
|
||||
-- path handling - only comes here when we've arrived at a new path square
|
||||
-- (note, the range passed to nearest_junction 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_junction(state.pos, nil, 10)
|
||||
if curpathnode and curpathnode.pos.x == state.pos.x and curpathnode.pos.z == state.pos.z then
|
||||
|
@ -300,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
|
||||
|
@ -379,11 +384,12 @@ people.actions.go = function(state)
|
|||
end
|
||||
|
||||
if unwholesome then
|
||||
if distance < 32 then
|
||||
if distance <= MAX_PATH_DIST then
|
||||
|
||||
if state.action.triedpath then
|
||||
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
|
||||
|
@ -407,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...
|
||||
|
|
|
@ -45,7 +45,9 @@ people.actions.obtain = function(state)
|
|||
local where = nil
|
||||
if state.action.item == "default:wood" then
|
||||
where = "Building Supplies 2"
|
||||
elseif state.action.item == "default:stone" then
|
||||
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
|
||||
|
|
|
@ -46,30 +46,44 @@ people.actions.place = function(state)
|
|||
end
|
||||
|
||||
-- We need a node to build against...
|
||||
local todir = state.action.againstdir
|
||||
if not todir then
|
||||
local trydirs
|
||||
if state.action.againstdir then
|
||||
trydirs = {againstdir}
|
||||
else
|
||||
trydirs = {{x=0, y=-1, z=0},
|
||||
{x=0, y=1, z=0},
|
||||
{x=-1, y=0, z=0},
|
||||
{x=1, y=0, z=0},
|
||||
{x=0, y=0, z=-1},
|
||||
{x=0, y=0, z=1}}
|
||||
end
|
||||
for _, trydir in pairs(trydirs) do
|
||||
local n = minetest.get_node(vector.add(state.action.pos, trydir))
|
||||
if n.name ~= "air" then
|
||||
todir = trydir
|
||||
break
|
||||
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
|
||||
if not todir then
|
||||
dbg.v1(state.ent.name.." can't place - nothing to build against")
|
||||
return true, false
|
||||
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",
|
||||
|
|
|
@ -24,7 +24,7 @@ people.actions.tunnel = function(state)
|
|||
dbg.v1(state.ent.name.." has invalid dir for tunnel action")
|
||||
return true, false
|
||||
end
|
||||
if state.action.dir.y ~= 0 then
|
||||
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
|
||||
|
@ -41,7 +41,31 @@ people.actions.tunnel = function(state)
|
|||
state.action.startdist = state.action.distance
|
||||
end
|
||||
|
||||
if(state.action.distance <= 0) then
|
||||
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
|
||||
|
@ -57,13 +81,6 @@ people.actions.tunnel = function(state)
|
|||
digpos.y = digpos.y - 1
|
||||
end
|
||||
|
||||
local relxaxis
|
||||
if state.action.dir.x == 0 then
|
||||
relxaxis = "x"
|
||||
else
|
||||
relxaxis = "z"
|
||||
end
|
||||
|
||||
local height = 2
|
||||
if state.action.dir.y ~= 0 then
|
||||
if state.action.steps then
|
||||
|
@ -73,6 +90,15 @@ people.actions.tunnel = function(state)
|
|||
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
|
||||
|
@ -87,20 +113,31 @@ people.actions.tunnel = function(state)
|
|||
end
|
||||
|
||||
-- "Special" positions are those where supports and lighting are placed
|
||||
local special_sup = (state.action.startdist - state.action.distance + 2) % 8 == 7
|
||||
local special_light = (state.action.startdist - state.action.distance) % 4 == 3
|
||||
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 = {x=0, y=y, z=0}
|
||||
thisoff[relxaxis] = x
|
||||
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
|
||||
|
||||
|
@ -113,7 +150,8 @@ people.actions.tunnel = function(state)
|
|||
return false
|
||||
end
|
||||
|
||||
elseif y == 2 and x ~= 0 and state.action.lighting and special_light then
|
||||
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.
|
||||
|
@ -124,7 +162,8 @@ people.actions.tunnel = function(state)
|
|||
return false
|
||||
end
|
||||
|
||||
elseif y == height and state.action.support_top and special_sup then
|
||||
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}
|
||||
|
@ -134,29 +173,13 @@ people.actions.tunnel = function(state)
|
|||
return false
|
||||
end
|
||||
|
||||
elseif x ~= 0 and state.action.support_side and special_sup then
|
||||
elseif x ~= 0 and not openside and state.action.support_side
|
||||
and special_sup then
|
||||
|
||||
if dignode.name == "air" then
|
||||
local param2
|
||||
if state.action.support_side_facein then
|
||||
local facevec = rotate({x=-x, y=0, z=0},
|
||||
state.action.dir)
|
||||
if facevec.x > 0 then
|
||||
param2 = 4 * 4
|
||||
elseif facevec.x < 0 then
|
||||
param2 = 4 * 3
|
||||
elseif facevec.z > 0 then
|
||||
param2 = 4 * 2
|
||||
else
|
||||
param2 = 4 * 1
|
||||
end
|
||||
end
|
||||
state.action = {"place", pos=thispos,
|
||||
item=state.action.support_side,
|
||||
successaction=state.action,
|
||||
param2=param2}
|
||||
return false
|
||||
elseif dignode.name ~= state.action.support_side then
|
||||
-- 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
|
||||
|
@ -175,8 +198,7 @@ people.actions.tunnel = function(state)
|
|||
|
||||
if state.action.floor and state.action.dir.y == 0 then
|
||||
for x = -1, 1 do
|
||||
local thisoff = {x=0, y=-1, z=0}
|
||||
thisoff[relxaxis] = x
|
||||
local 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
|
||||
|
@ -195,11 +217,18 @@ people.actions.tunnel = function(state)
|
|||
wall = state.action.support_wall
|
||||
end
|
||||
|
||||
if wall then
|
||||
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
|
||||
local thisoff = {x=0, y=y, z=0}
|
||||
for x = -2, 2, 4 do
|
||||
thisoff[relxaxis] = x
|
||||
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
|
||||
|
@ -213,15 +242,23 @@ people.actions.tunnel = function(state)
|
|||
end
|
||||
end
|
||||
|
||||
if state.action.lighting and special_light then
|
||||
local thisoff = {x=0, y=2, z=0}
|
||||
for x = -1, 1, 2 do
|
||||
thisoff[relxaxis] = x
|
||||
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 = {x=0, y=0, z=0}
|
||||
againstdir[relxaxis] = x
|
||||
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
|
||||
|
@ -230,24 +267,64 @@ people.actions.tunnel = function(state)
|
|||
|
||||
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
|
||||
-- We need to place the ceiling nodes against something - we use
|
||||
-- the walls.
|
||||
local againstdir = {x=0, y=0, z=0}
|
||||
againstdir[relxaxis] = -1
|
||||
for x = -1, 1 do
|
||||
local thisoff = {x=0, y=height+1, z=0}
|
||||
thisoff[relxaxis] = x
|
||||
local 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,
|
||||
againstdir=againstdir}
|
||||
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}
|
||||
|
|
16
commands.lua
16
commands.lua
|
@ -56,6 +56,20 @@ subcmd.help = {
|
|||
end
|
||||
}
|
||||
|
||||
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 reload presets"
|
||||
end
|
||||
|
||||
people.load_presets()
|
||||
return true, "Reloaded presets"
|
||||
end
|
||||
}
|
||||
|
||||
subcmd.create = {
|
||||
params = "<name>",
|
||||
desc = "Create a person with the given name at your current location",
|
||||
|
@ -211,7 +225,7 @@ subcmd.attach = {
|
|||
if not ent then
|
||||
return false, nil, true
|
||||
end
|
||||
player:set_attach(ent.object, "", {x=0,y=0,z=0}, {x=0,y=0,z=0})
|
||||
player:set_attach(ent.object, "", {x=0,y=1,z=0}, {x=0,y=0,z=0})
|
||||
return true, "Attached to "..person_name
|
||||
end
|
||||
}
|
||||
|
|
56
init.lua
56
init.lua
|
@ -38,24 +38,26 @@ 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,
|
||||
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"
|
||||
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
|
||||
|
@ -711,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))
|
||||
|
@ -732,15 +746,7 @@ people.exec_event = function(ent, event)
|
|||
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
|
||||
|
|
|
@ -7,11 +7,28 @@ 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}},
|
||||
|
@ -28,8 +45,6 @@ local inventory_actions = {
|
|||
}
|
||||
}},
|
||||
|
||||
{"go", name="Miner Willy's"}
|
||||
|
||||
}
|
||||
|
||||
if event.type == "program" then
|
||||
|
@ -66,20 +81,43 @@ elseif event.type == "act" then
|
|||
|
||||
if mem.mode == MODE_selling then
|
||||
|
||||
if carrying("default:iron_lump") > 0 then
|
||||
action = {"go", name="Mineral Shop", successaction={"sell", item="default:iron_lump"}}
|
||||
if mem.cur > #sell_items then
|
||||
action = {"wait", time=0.5}
|
||||
mem.mode = MODE_inventory
|
||||
mem.cur = 1
|
||||
return
|
||||
end
|
||||
-- TODO - need to sell excess coal here, but the sell action currently
|
||||
-- doesn't allow an amount to be specified, so we'd sell it all!
|
||||
mem.mode = MODE_inventory
|
||||
mem.cur = 1
|
||||
action = {"wait", time=1}
|
||||
|
||||
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
|
||||
action={"obtain", item=mem.needed[1], num=1, prevaction={"wait", time=1}}
|
||||
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
|
||||
|
@ -93,7 +131,7 @@ elseif event.type == "act" then
|
|||
elseif mem.lastxmode == MODE_mining then
|
||||
mem.mode = MODE_homebuilding
|
||||
elseif mem.lastxmode == MODE_homebuilding then
|
||||
mem.mode = MODE_mining
|
||||
mem.mode = MODE_minebuilding
|
||||
end
|
||||
mem.lastxmode = mem.mode
|
||||
mem.cur = 1
|
||||
|
@ -101,63 +139,247 @@ elseif event.type == "act" then
|
|||
|
||||
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
|
||||
action = {"go", name="Miner Willy's",
|
||||
successaction={"gather", items={"default:torch"}}}
|
||||
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
|
||||
|
||||
-- Select some materials to fancy up the tunnel, if we're carrying
|
||||
-- them. If not, we'll do it without them and they'll get done
|
||||
-- another time.
|
||||
local steps = nil
|
||||
local wall_and_floor = nil
|
||||
local support_c = nil
|
||||
local support_s = nil
|
||||
local lighting = nil
|
||||
if carrying("stairs:stair_cobble") > 3 then
|
||||
steps = "stairs:stair_cobble"
|
||||
if carrying("default:wood") > 3 then
|
||||
support_c = "default:wood"
|
||||
end
|
||||
if carrying("stairs:slab_wood") > 3 then
|
||||
support_s = "stairs:slab_wood"
|
||||
end
|
||||
if carrying("default:stone") > 10 then
|
||||
wall_and_floor = "default:stone"
|
||||
end
|
||||
end
|
||||
if carrying("default:torch") > 1 then
|
||||
lighting = "default:torch"
|
||||
end
|
||||
-- We'd better be facing the right way!!
|
||||
local bdir = vector.round(vector.from_speed_yaw(1, yaw))
|
||||
|
||||
action = {"tunnel", distance=40, dir={x=0,y=-1,z=-1}, steps=steps,
|
||||
-- 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}
|
||||
action.successaction= {"tunnel", distance=8, dir={x=0,y=0,z=-1},
|
||||
lighting=lighting, floor=wall_and_floor,
|
||||
wall=wall_and_floor, floor=wall_and_floor,
|
||||
ceiling=wall_and_floor}
|
||||
action.successaction.successaction={"go", rel={x=1,y=0,z=2}}
|
||||
table.insert(mem.exitmine, {"go", name="Miner Willy's"})
|
||||
mem.cur = 3
|
||||
|
||||
elseif mem.cur == 3 then
|
||||
|
||||
action = {"tunnel", distance=20, dir={x=1,y=-1,z=0}, steps="stairs:stair_cobble",
|
||||
lighting="default:torch", wall="default:stone", floor="default:stone"}
|
||||
action.successaction=
|
||||
{"tunnel", distance=8, dir={x=1,y=0,z=0}, lighting="default:torch", successaction=
|
||||
{"go", rel={x=-1,y=0,z=1}} }
|
||||
table.insert(mem.exitmine, {"go", pos=vector.subtract(pos, {x=1,y=0,z=0})})
|
||||
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
|
||||
|
@ -173,18 +395,30 @@ elseif event.type == "act" then
|
|||
|
||||
elseif mem.mode == MODE_minebuilding then
|
||||
|
||||
local mine_jctname = "people_"..name.."_mine"
|
||||
|
||||
if mem.cur == 1 then
|
||||
action = {"go", name="Miner Willy's"}
|
||||
-- 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 = {"building", dir={x=0,y=-1,z=-1},
|
||||
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 = 3
|
||||
mem.cur = 4
|
||||
else
|
||||
mem.mode = MODE_selling
|
||||
mem.cur = 1
|
||||
|
@ -208,10 +442,12 @@ elseif event.type == "act" then
|
|||
-- We'd better be facing the right way!!
|
||||
local bdir = vector.round(vector.from_speed_yaw(1, yaw))
|
||||
|
||||
action = {"building", dir=bdir,
|
||||
width=5, depth=5, height=4,
|
||||
wall="default:stone",
|
||||
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"
|
||||
}
|
||||
|
@ -226,6 +462,5 @@ elseif event.type == "act" then
|
|||
action = {"wait", time=600}
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue