More latest stuff

master
Ciaran Gultnieks 2015-05-04 19:20:38 +01:00
parent 70f79c0fc2
commit cb29cd13d7
10 changed files with 421 additions and 25 deletions

View File

@ -80,6 +80,8 @@ In addition:
* `log(msg)` - logs a v1 debug message
* `mem` - the person's memory - see under Persistence
* `carrying(item)` - returns the number of a particluar item the person is
carrying - e.g. carrying("default:cobble") might return 0, 10, 99 or 198.
### Persistence
@ -128,6 +130,10 @@ This happens when the person has no current action (see the Actions section).
The handler should select a new action, by setting the 'action' variable. If
it fails to do so, the person will do an automatic wait.
### actfail
This happens when an action fails.
### tell
This happens when a player (who must be the owner, current) sends a message
@ -246,17 +252,38 @@ Each item in the list can be:
* Item name preceded by an asterisk (e.g. "\*default:cobble") , to get all
of that kind of item out
#### sell
* Required parameter: item="itemstring"
Need to be standing within range of a shop (barrel mod) that buys the item
in question.
The entire amount of the item carried will be sold, assuming the shop can
afford to buy it all.
#### buy
* Required parameter: item="itemstring"
* 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.
#### gather
Changes current gathering settings.
All parameters are optional:
* `nodes = {}` - list of nodes to dig
* `topnodes = {}` - list of nodes to dig only if there is a) air above,
and b) the same node below - think of harvesting cactus/papyrus while
still allowing them to grow back!
* `items = {}` - list of items to pick up
* `plant = ""` - seed to plant
* `nodes = {}` - list of nodes to dig
* `topnodes = {}` - list of nodes to dig only if there is a) air above,
and b) the same node below - think of harvesting
cactus/papyrus while still allowing them to grow back!
* `items = {}` - list of items to pick up
* `plant = nil - seed to plant
* animal = nil - species of animal to harvest from
* animal_wield = nil - item to wield when harvesting
So calling with no parameters at all turns off gathering.
@ -278,11 +305,21 @@ The action will cancel when the player is out of range.
#### pickup
Picks up an item. Paramaters:
Picks up an item. Parameters:
* `pos = {x,y,z}` - position of the item (required)
* `item = "name"` - name of item, e.g. "default:dirt"
#### harvest
Harvest from an animal from the animals mod. Parameters:
* name = "name" - the name of the animal (e.g. "cow 1452")
The correct item should already be wielded. This is normally used in
conjunction with gathering, in which case the wielding is already
handled.
#### 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

141
actions/buy_and_sell.lua Normal file
View File

@ -0,0 +1,141 @@
local dbg
if moddebug then dbg=moddebug.dbg("people") else dbg={v1=function() end,v2=function() end,v3=function() end} end
-- Find a shop in range of the given position that's dealing in the
-- item specified. If 'buy' is true, we want to buy, otherwise we
-- want to sell. Returns shopinfo. (or nil)
local function near_shop(pos, item, buy)
local range = {x=4, y=2, z=4}
local minp = vector.subtract(pos, range)
local maxp = vector.add(pos, range)
local shops = minetest.find_nodes_in_area(minp, maxp, {"barrel:shop_barrel"})
for _, p in pairs(shops) do
local shopinfo = barrel.get_shopinfo(p)
if shopinfo and shopinfo.item == item then
if buy then
if shopinfo.saleunit > 0 and
shopinfo.price > 0 and
shopinfo.stock >= shopinfo.saleunit then
return shopinfo
end
else
if shopinfo.saleunit > 0 and
shopinfo.buyprice > 0 then
return shopinfo
end
end
end
end
return nil
end
local function move_all(srcinv, srclist, destinv, destlist, item)
local moved_all = true
local moved_num = 0
for i=1,srcinv:get_size(srclist) do
local stk = srcinv:get_stack(srclist, i)
if not item or stk:get_name() == item then
if destinv:room_for_item(destlist, stk) then
moved_num = moved_num + stk:get_count()
srcinv:set_stack(srclist, i, nil)
destinv:add_item(destlist, stk)
else
moved_all = false
end
end
end
return moved_all, moved_num
end
people.actions.buy = function(state)
if not (state.action.item or state.action.num) then
dbg.v1(state.ent.name.." has invalid buy action")
return true, false
end
local shopinfo = near_shop(state.pos, state.action.item, true)
if not shopinfo then
dbg.v1(state.ent.name.." is not near an appropriate shop")
return true, false
end
local yaw = vector.get_yaw(state.pos, shopinfo.pos)
-- Need to be fuzzy about comparing this, because of the
-- lua double/minetest float issue
if math.abs(yaw - state.yaw) > 0.2 then
dbg.v3(state.ent.name.." turning to face shop")
state.action = {"face", yaw=yaw, prevaction=state.action}
return false
end
local shopinv = shopinfo.meta:get_inventory()
local repeats = math.ceil(state.action.num / shopinfo.saleunit)
local cashneeded = repeats * shopinfo.price
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
end
-- TODO - the following just assumes there's room for it - really need
-- to check and roll back, as we do for getting money
currency.put_to_inv(shopinv, "exchange", cashneeded)
while repeats > 0 do
if not barrel.do_buy(shopinfo.pos) then break end
repeats = repeats - 1
end
if not move_all(shopinv, "exchange", state.ent.inventory, "main", nil) then
dbg.v1(state.ent.name.." couldn't retrieve everything from shop inventory")
end
dbg.v1(state.ent.name.." finished buy action")
return true, true
end
people.actions.sell = function(state)
if not (state.action.item) then
dbg.v1(state.ent.name.." has invalid sell action")
return true, false
end
local shopinfo = near_shop(state.pos, state.action.item, false)
if not shopinfo then
dbg.v1(state.ent.name.." is not near an appropriate shop")
return true, false
end
local yaw = vector.get_yaw(state.pos, shopinfo.pos)
-- Need to be fuzzy about comparing this, because of the
-- lua double/minetest float issue
if math.abs(yaw - state.yaw) > 0.2 then
dbg.v3(state.ent.name.." turning to face shop")
state.action = {"face", yaw=yaw, prevaction=state.action}
return false
end
local shopinv = shopinfo.meta:get_inventory()
local moved_all, num = move_all(state.ent.inventory, "main",
shopinv, "exchange", state.action.item)
if num == 0 then
dbg.v1(state.ent.name.." failed to put any "..state.action.item.." for sale into the shop")
return true, false
end
local repeats = math.floor(num / shopinfo.saleunit)
while repeats > 0 do
if not barrel.do_sell(shopinfo.pos) then break end
repeats = repeats - 1
end
if not move_all(shopinv, "exchange", state.ent.inventory, "main") then
dbg.v1(state.ent.name.." couldn't retrieve everything from shop inventory")
end
dbg.v1(state.ent.name.." finished sell action")
return true, true
end

View File

@ -5,6 +5,8 @@ people.actions.gather = function(state)
state.gather.topnodes = state.action.topnodes or {}
state.gather.items = state.action.items or {}
state.gather.plant = state.action.plant
state.gather.animal = state.action.animal
state.gather.animal_wield = state.action.animal_wield
return true, true
end

View File

@ -233,7 +233,8 @@ people.actions.go = function(state)
local colpos = col.node_p
local colnode = minetest.get_node(colpos)
local door = minetest.registered_nodes[colnode.name].groups.door
if door and door ~= 0 then
local gate = minetest.registered_nodes[colnode.name].groups.gate
if (door and door ~= 0) or (gate and gate ~= 0) then
state.action = {"rightclicknode", pos=colpos, prevaction=state.action}
return false
end
@ -285,7 +286,9 @@ people.actions.go = function(state)
-- 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 then
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

63
actions/harvest.lua Normal file
View File

@ -0,0 +1,63 @@
local dbg
if moddebug then dbg=moddebug.dbg("people") else dbg={v1=function() end,v2=function() end,v3=function() end} end
people.actions.harvest = function(state)
if not state.action.name or type(state.action.name) ~= "string" then
dbg.v1(state.ent.name.." has invalid harvest action")
return true, false
end
local hitit = nil
local objects = minetest.get_objects_inside_radius(state.pos, 6)
for k, obj in ipairs(objects) do
if not obj:is_player() then
local ent = obj:get_luaentity()
if ent and ent.name == state.action.name then
hitit = ent
break
end
end
end
if not hitit then
dbg.v1(state.ent.name.." can't find "..state.action.name)
return true, false
end
local yaw = vector.get_yaw(state.pos, hitit.object:getpos())
-- Need to be fuzzy about comparing this, because of the
-- lua double/minetest float issue
if math.abs(yaw - state.yaw) > 0.2 then
dbg.v3(state.ent.name.." turning to face pickup position")
state.action = {"face", yaw=yaw, prevaction=state.action}
return false
end
state.anim = "mine"
if not state.action.harvesttime then
state.action.harvesttime = 1
return false
end
state.action.harvesttime = state.action.harvesttime - state.dtime
if state.action.harvesttime > 0 then
return false
end
local spinfo = animals.species[hitit.species]
if not spinfo.is_harvestable or not spinfo.is_harvestable(hitit) then
dbg.v1(state.ent.name.." can't harvest from "..state.action.name)
return true, false
end
-- Actually do it, if it's still there
spinfo.on_harvest(hitit, people.get_fake_player(state.ent))
dbg.v1(state.ent.name.." harvested from "..state.action.name)
return true, false
end

View File

@ -12,7 +12,7 @@ people.actions.place = function(state)
local distance = vector.distance(state.pos, state.action.pos)
if distance > 5 then
dbg.v1(state.ent.name.." too far away to place "..minetest.pos_to_string(foundpos))
dbg.v1(state.ent.name.." too far away to place - distance is "..distance)
return true, false
end

View File

@ -209,6 +209,9 @@ subcmd.track = {
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.hud_show_by_player[playername] then
return false, "No person was being tracked"
end

View File

@ -275,9 +275,20 @@ people.footpath_findnext = function(curpos, lastpos, samedironly)
--dbg.v3("footpath_findnext found (snow-covered) "..minetest.pos_to_string(npos))
return npos
end
-- Otherwise, so long as there's nothing walkable above the
-- node it should be a valid footpath node...
if not minetest.registered_nodes[n.name].walkable then
-- TODO - allowing doors/gates here, as we do elsewhere, but
-- really we need to know if the entity can use doors!
local nd = minetest.registered_nodes[n.name]
local iswalkable = nd.walkable
if iswalkable and (
(nd.groups.door and nd.groups.door ~= 0) or
(nd.groups.gate and nd.groups.gate ~= 0)) then
iswalkable = false
end
if not iswalkable then
--dbg.v3("footpath_findnext found "..minetest.pos_to_string(npos))
return npos
end

View File

@ -14,7 +14,8 @@ dofile(people_modpath .. "/footpath.lua")
-- Contains all action handlers. Each action handler takes a 'state' table as
-- a parameter, and returns true if the current action is complete, false
-- otherwise.
-- otherwise. When complete, a second boolean is also returned which says
-- whether the action completed succesfully, or failed due to an error.
people.actions = {}
dofile(people_modpath .. "/actions/step.lua")
dofile(people_modpath .. "/actions/face.lua")
@ -26,9 +27,11 @@ dofile(people_modpath .. "/actions/dig.lua")
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/attackplayer.lua")
dofile(people_modpath .. "/actions/pickup.lua")
dofile(people_modpath .. "/actions/rightclicknode.lua")
dofile(people_modpath .. "/actions/harvest.lua")
people.footpath_load()
@ -37,6 +40,7 @@ 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
@ -169,10 +173,12 @@ minetest.register_entity("people:person" ,{
-- picking up of items, which happens in parallel with any other action.
--
self.gather = {
nodes = {}, -- list of nodes to dig up
topnodes = {}, -- list of topnodes to dig
items = {}, -- list of items to pick up
plant = nil, -- name of seed to plant, or nil
nodes = {}, -- list of nodes to dig up
topnodes = {}, -- list of topnodes to dig
items = {}, -- list of items to pick up
plant = nil, -- name of seed to plant, or nil
animal = nil, -- name of animal to gather from
animal_wield = nil, -- when animal set, the item to wield
}
self.name = "<new person>"
@ -341,7 +347,7 @@ minetest.register_entity("people:person" ,{
self.action = {"wait", time=120}
dbg.v2(self.name.." failed to choose action - setting wait, at "..minetest.pos_to_string(pos))
else
dbg.v1(self.name.." set new action: "..dump(self.action))
dbg.v1(self.name.." set new action: "..minetest.serialize(self.action))
end
end
@ -354,7 +360,7 @@ minetest.register_entity("people:person" ,{
self.action[1] == "follow" or
self.action[1] == "wait") and
(#self.gather.items + #self.gather.nodes + #self.gather.topnodes > 0 or
self.gather.plant) then
self.gather.plant or self.gather.animal) then
local dist = 99
if self.gather.lastpos then
dist = vector.distance(pos, self.gather.lastpos)
@ -364,7 +370,7 @@ minetest.register_entity("people:person" ,{
local done = false
if #self.gather.nodes > 0 then
dbg.v2(self.name.." looking for "..dump(self.gather.nodes).." near "..minetest.pos_to_string(pos))
dbg.v2(self.name.." looking for "..minetest.serialize(self.gather.nodes).." near "..minetest.pos_to_string(pos))
local foundpos = minetest.find_node_near(
vector.add(vector.round(pos), {x=0,y=1,z=0}),
3, self.gather.nodes)
@ -376,7 +382,7 @@ minetest.register_entity("people:person" ,{
end
end
if not done and #self.gather.topnodes > 0 then
dbg.v2(self.name.." looking for topnodes "..dump(self.gather.topnodes).." near "..minetest.pos_to_string(pos))
dbg.v2(self.name.." looking for topnodes "..minetest.serialize(self.gather.topnodes).." near "..minetest.pos_to_string(pos))
local minp = vector.add(pos, {x=-3,y=-3,z=-3})
local maxp = vector.add(pos, {x=3, y=3, z=3})
local pp = minetest.find_nodes_in_area(minp, maxp, self.gather.topnodes)
@ -394,7 +400,7 @@ minetest.register_entity("people:person" ,{
end
end
if not done and #self.gather.items > 0 then
dbg.v2(self.name.." looking for items "..dump(self.gather.items).." near "..minetest.pos_to_string(pos))
dbg.v2(self.name.." looking for items "..minetest.serialize(self.gather.items).." near "..minetest.pos_to_string(pos))
local objects = minetest.get_objects_inside_radius(
vector.add(vector.round(pos), {x=0,y=1,z=0}), 6)
for _, obj in ipairs(objects) do
@ -443,6 +449,41 @@ minetest.register_entity("people:person" ,{
end
end
if not done and animals and (self.gather.animal and self.gather.animal_wield) then
if self.inventory:contains_item("main", ItemStack(self.gather.animal_wield)) then
self.wielded = self.gather.animal_wield
local spinfo = animals.species[self.gather.animal]
if not spinfo then
dbg.v1(self.name.." trying to harvest unknown species "..self.gather.animal)
self.gather.animal = nil
self.gather.animal_wield = nil
else
local hitit = nil
local objects = minetest.get_objects_inside_radius(pos, 5)
for k, obj in ipairs(objects) do
if not obj:is_player() then
local ent = obj:get_luaentity()
if ent then
dbg.v3(self.name.." examining "..(ent.name or "<noname>"))
if ent.species == self.gather.animal then
if spinfo.is_harvestable and spinfo.is_harvestable(ent) then
hitit = ent
break
end
end
end
end
end
if hitit then
dbg.v1(self.name.." decided to harvest "..hitit.name)
self.action = {"harvest", name=hitit.name, prevaction=self.action}
done = true
end
end
end
end
if not done then
-- We didn't find anything to do here, so we can move on
self.gather.lastpos = pos
@ -501,14 +542,14 @@ minetest.register_entity("people:person" ,{
action_done, action_success = handler(state)
-- Because state.action can be replaced...
if state.action ~= self.action then
dbg.v3(state.ent.name.." action handler replaced action with "..dump(state.action))
dbg.v3(state.ent.name.." action handler replaced action with "..minetest.serialize(state.action))
self.action = state.action
end
if state.gather ~= self.gather then
self.gather = state.gather
end
else
dbg.v1(state.ent.name.." has unknown action: "..dump(self.action))
dbg.v1(state.ent.name.." has unknown action: "..minetest.serialize(self.action))
self.action = nil
end
@ -661,7 +702,7 @@ end
people.exec_event = function(ent, event)
dbg.v3(ent.name.." exec event "..dump(event))
dbg.v3(ent.name.." exec event "..minetest.serialize(event))
local code = ent.code
if string.sub(code, 1, 1) == "@" then
@ -678,6 +719,21 @@ people.exec_event = function(ent, event)
env.action = ent.action
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
end
env.has_money = function()
local inv = ent.inventory
return currency.count_money(inv, "main")
end
env.event = event
@ -804,7 +860,12 @@ people.get_fake_player = function(ent)
getpos = defer(ent.object:getpos()),
get_hp = defer(ent.object:get_hp()),
get_inventory = defer(ent.inventory),
get_wielded_item = function() return ItemStack("") end,
get_wielded_item = function()
if ent.wielded and ent.inventory:contains_item("main", ent.wielded) then
return ItemStack(ent.wielded)
end
return ItemStack("")
end,
get_wield_index = defer(),
get_wield_list = defer("main"),
moveto = defer(),

75
presets/CowTest.lua Normal file
View File

@ -0,0 +1,75 @@
if event.type == "program" then
mem.cur = 0
mem.actions = {
{"go", name="cowfarm"},
{"wait", time=10},
{"go", pos={x=39,y=11.5,z=189}},
{"wait", time=10},
{"gather", animal="cow",
animal_wield="vessels:drinking_glass"},
{"go", pos={x=54,y=11.5,z=181}},
{"wait", time=10},
{"go", pos={x=54,y=11.5,z=193}},
{"wait", time=10},
{"go", pos={x=40,y=11.5,z=193}},
{"wait", time=10},
{"go", pos={x=39,y=11.5,z=189}},
{"gather"},
{"wait", time=10},
{"go", name="cowfarm"},
}
elseif event.type == "actfail" then
mem.cur = -1
elseif event.type == "act" then
if mem.cur == -1 then
mem.cur = 0
action = {"go", name="cowfarmhouse",
prevaction = {"wait", time=600}}
elseif mem.cur == 0 then
if carrying("animalmaterials:milk") > 0 then
action = {"go", name="Farmer's Market",
prevaction={"sell", item="animalmaterials:milk"}}
return
end
if carrying("vessels:drinking_glass") == 0 then
if has_money() < 5 then
-- TODO - backup plan to get some money here!
action = {"go", name="cowfarm",
prevaction={"wait", time=120}}
end
action = {"go", name="Cookshop",
prevaction={"buy", item="vessels:drinking_glass", num=5}}
return
else
mem.cur = 1
action = {"wait", time=5}
end
else
action = mem.actions[mem.cur]
mem.cur = mem.cur + 1
if mem.cur > #mem.actions then
mem.cur = 0
end
end
end