diff --git a/actions/face.lua b/actions/face.lua index 3e26c0a..fe283fc 100644 --- a/actions/face.lua +++ b/actions/face.lua @@ -4,15 +4,17 @@ if moddebug then dbg=moddebug.dbg("people") else dbg={v1=function() end,v2=funct people.actions.face = function(state) if state.action.pos and state.action.pos.x and state.action.pos.y and state.action.pos.z then - state.yaw = vector.get_yaw(statepos, state.action.pos) - dbg.v3(state.ent.name.." setting calculated yaw to "..state.action.yaw) - elseif state.action.yaw then - dbg.v3(state.ent.name.." setting yaw to "..state.action.yaw) - state.yaw = state.action.yaw - else - dbg.v1(state.ent.name.." has invalid face action "..dump(state.action)) + state.yaw = vector.get_yaw(state.pos, state.action.pos) + dbg.v3(state.ent.name.." setting calculated yaw to "..state.yaw) + return true, true + elseif state.action.yaw then + dbg.v3(state.ent.name.." setting yaw to "..state.action.yaw) + state.yaw = state.action.yaw + return true, true end - return true, true + + dbg.v1(state.ent.name.." has invalid face action "..dump(state.action)) + return true, false end diff --git a/actions/go.lua b/actions/go.lua index 795e736..5d99988 100644 --- a/actions/go.lua +++ b/actions/go.lua @@ -2,11 +2,15 @@ local dbg if moddebug then dbg=moddebug.dbg("people") else dbg={v1=function() end,v2=function() end,v3=function() end} end --- "Path Node To Pos". Footpath nodes are 1x1 areas ABOVE the path. People +-- "Area Node To Pos". Footpath area nodes are 1x1 areas ABOVE the path. People -- positions (to match player positions) are 0.5 above the node they are -- standing on. Add this to the Y coordinate to convert from a path node -- position to a people position. -local PNTP = -0.5 +local ANTP = -0.5 + +-- "Path Node To Pos". But the nodes that form the actual path are below. +-- This is all very messy and needs rationalising!! +local PNTP = 0.5 people.actions.go = function(state) @@ -38,7 +42,7 @@ people.actions.go = function(state) if pathstartarea then dbg.v2(state.ent.name.." selected path start "..pathstartarea.name.." at "..minetest.pos_to_string(pathstartarea.pos1)) state.action.pos = vector.new(pathstartarea.pos1) - state.action.pos.y = state.action.pos.y + PNTP + state.action.pos.y = state.action.pos.y + ANTP state.action.footpathdest = pathdestarea return false end @@ -97,7 +101,7 @@ people.actions.go = function(state) return true, false end dbg.v2(state.ent.name.." leaving junction via "..nextpathdest.name) - state.action.pos = vector.add(nextpathdest.pos1, {x=0, y=PNTP, z=0}) + state.action.pos = vector.add(nextpathdest.pos1, {x=0, y=ANTP, z=0}) return false end @@ -111,6 +115,11 @@ people.actions.go = function(state) 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 = people.footpath_findnext(thispathpos, state.action.lastpathpos, false) + if nextpathpos == "unloaded" then + state.wait = 1 + dbg.v3(state.ent.name.." waiting for block load") + return false + end if not nextpathpos then dbg.v1(state.ent.name.." can't find next footpath node at ".. minetest.pos_to_string(thispathpos).." after ".. @@ -118,10 +127,15 @@ people.actions.go = function(state) return true, false end - if not distance then distance = 10 end + if not distance or distance > 10 then distance = 10 end while distance > 2 do -- Look ahead a bit while going in a straight line, to increase speed local aheadpathpos = people.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 thispathpos = nextpathpos nextpathpos = aheadpathpos @@ -131,7 +145,7 @@ people.actions.go = function(state) end end state.action.lastpathpos = thispathpos - state.action.pos = vector.add(nextpathpos, {x=0, y=PNTP, z=0}) + state.action.pos = vector.add(nextpathpos, {x=0, y=0.5, z=0}) dbg.v3(state.ent.name.." set next footpath dest "..minetest.pos_to_string(state.action.pos)) return false @@ -167,7 +181,7 @@ people.actions.go = function(state) if pathdestarea and psdist < distance then state.action.endpos = state.action.pos state.action.pos = vector.new(pathstartarea.pos1) - state.action.pos.y = state.action.pos.y + PNTP + state.action.pos.y = state.action.pos.y + ANTP state.action.footpathdest = pathdestarea dbg.v2(state.ent.name.." using footpath from "..pathstartarea.name.." to "..pathdestarea.name.." for intermediate route") return false @@ -188,12 +202,13 @@ people.actions.go = function(state) dbg.v2(state.ent.name.." arrived near "..minetest.pos_to_string(curdest)) state.setpos = vector.round(curdest) state.setpos.y = state.pos.y - if state.action.footpathdest or state.action.intermediate then - -- If we arrived at the pos, but we still have a - -- pathdest or intermediate dest, we carry on to do that. - if state.action.footpathdest then - state.action.pos = nil - end + -- If we arrived at the pos, but we still have a + -- pathdest or intermediate dest, we carry on to do that. + if state.action.intermediate then + state.action.pos = state.action.intermediate[1] + return false + elseif state.action.footpathdest then + state.action.pos = nil return false end -- We've finished! @@ -215,25 +230,29 @@ people.actions.go = function(state) -- Apply some damage for bashing into something. Apart from being -- the right thing to do, it will also kill of things that get -- stuck in a wall. ;) - state.damage = 0.5 + state.damage = 0.4 end - if not unwholesome and distance > 1 then + if not unwholesome and distance > 1 then local erk = nil local ahead = state.speed 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 + 1 + 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 = 5 + 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 == "default:water_flowing" or nn == "default:water_source" or nn == "default:lava_flowing" or nn == "default:lava_source" then + if nn == "ignore" then + unloaded = true + elseif nn == "default:water_flowing" or nn == "default:water_source" or nn == "default:lava_flowing" or nn == "default:lava_source" then erk = nn break end @@ -242,13 +261,26 @@ people.actions.go = function(state) erk = "mysterious " .. nn break end - if nd.walkable then + if nd.walkable and ncount < 6 then break end + if not nd.walkable then + notwalkable = notwalkable + 1 + end nextpos.y = nextpos.y - 1 ncount = ncount - 1 end - if ncount == 0 then erk = "fall" 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) @@ -258,7 +290,17 @@ people.actions.go = function(state) if unwholesome then if distance < 32 then - local path = minetest.find_path(state.pos, curdest, + + 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 + end + + local spos = vector.new(state.pos) + if state.ent.yoffset then + spos.y = spos.y - state.ent.yoffset + end + local path = minetest.find_path(spos, curdest, distance + 16, 1, 3, "A*_noprefetch") if not path then @@ -272,13 +314,21 @@ people.actions.go = function(state) return true, false else + if not state.action.intermediate or #state.action.intermediate > 10 then state.action.intermediate = {} end - for i = #path, 2, -1 do - table.insert(state.action.intermediate, 1, {x=path[i].x, y=path[i].y + PNTP, z=path[i].z}) + + -- Start following the path from the rounded position, in case + -- we're off centre and will collide immediately... + table.insert(state.action.intermediate, vector.round(state.pos)) + + for i = 2, #path do + table.insert(state.action.intermediate, {x=path[i].x, y=path[i].y -0.5, z=path[i].z}) end - dbg.v3(state.ent.name.." found path: "..dump(path)) + + dbg.v3(state.ent.name.." found path to "..minetest.pos_to_string(curdest).." : "..dump(path)) + state.action.triedpath = true return false end end @@ -307,6 +357,12 @@ people.actions.go = function(state) else state.speed = 3 end + + -- Apply max speed if there is one + if state.ent.maxspeed and state.speed > state.ent.maxspeed then + state.speed = state.ent.maxspeed + end + state.yaw = vector.get_yaw(state.pos, curdest) return false diff --git a/commands.lua b/commands.lua index 6d84b6f..764384f 100644 --- a/commands.lua +++ b/commands.lua @@ -101,6 +101,30 @@ subcmd.create = { end } +subcmd.forget = { + params = "", + desc = "Completely forget the person with the given name. If activated, they will be deleted.", + exec = function(playername, args) + + local person, person_name + person_name, person, args = get_person(args) + if not person then + return false, "Specify a valid person" + end + if not minetest.check_player_privs(playername, {server=true}) then + if playername ~= ent.owner then + return false, person_name.." isn't yours, so so can't do that" + end + end + local ent = people.people[person_name].entity + if ent then + ent.object:remove() + end + people.people[person_name] = nil + return true, "Forgot "..person_name + end +} + subcmd.delete = { params = "", desc = "Delete the person with the given name", diff --git a/footpath.lua b/footpath.lua index 1b9afff..2315709 100644 --- a/footpath.lua +++ b/footpath.lua @@ -240,7 +240,9 @@ end -- @param curpos The position of a current footpath node -- @param lastpos The position of the previous footpath node -- @param samedironly True to only look in the current direction of travel --- @return The position of the next footpath node, or nil +-- @return The position of the next footpath node, or nil if there isn't +-- one, or "unloaded" if there may or may not be one, but we can't +-- tell because of unloaded blocks. people.footpath_findnext = function(curpos, lastpos, samedironly) --dbg.v3("footpath_findnext, cur="..minetest.pos_to_string(curpos)..", last="..minetest.pos_to_string(lastpos)..", same="..dump(samedironly)) local xz @@ -260,7 +262,9 @@ people.footpath_findnext = function(curpos, lastpos, samedironly) local npos = vector.add(curpos, vector.new(x, y, z)) if not vector.equals(npos, lastpos) then local n = minetest.get_node(npos) + if n.name == "ignore" then return "unloaded" end if n.name == 'default:cobble' or + n.name == 'default:mossycobble' or n.name == 'stairs:stair_cobble' or n.name == 'default:wood' or n.name == 'stairs:stair_wood' then diff --git a/init.lua b/init.lua index 82f8c03..ff41ab7 100644 --- a/init.lua +++ b/init.lua @@ -141,42 +141,35 @@ minetest.register_entity("people:person" ,{ mesh = "people_character.x", textures = {"people_character.png"}, makes_footstep_sound = true, - name = "APerson", owner = "", code = "@"..people.presets[1].name, - memory = {}, - -- Current action. A table, as described in README.md -- Is nil when it's time for the person to select a new action. action = nil, - -- The following controls gathering, which is the digging of nodes and - -- picking up of items, which happens in parallel with any other action. - -- - 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 - }, - -- Time (game time) to wait until before doing anything else wait = 0, - -- Lua environment - env = {}, - stepheight = 1.1, - -- Customised properties - props = {}, - on_activate = function(self, staticdata) local setactive = false + self.memory = {} + self.env = {} + self.props = {} + -- The following controls gathering, which is the digging of nodes and + -- 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 + } + self.name = "" - dbg.v3("on_activate, staticdata = "..dump(staticdata)) local restored if staticdata and staticdata ~= "" then restored = minetest.deserialize(staticdata) @@ -251,6 +244,8 @@ minetest.register_entity("people:person" ,{ local err = people.exec_event(self, event) if err then dbg.v1("Lua error "..err) end + dbg.v3("Done on_activate for "..self.name) + end, update_props = function(self) @@ -318,11 +313,16 @@ minetest.register_entity("people:person" ,{ on_step = function(self, dtime) if self.wait ~= 0 then - if minetest.get_gametime() < self.wait then return end + if minetest.get_gametime() < self.wait then + self.object:setvelocity({x=0,y=-10,z=0}) + return + end dbg.v3(self.name.." finished wait at "..minetest.get_gametime()) self.wait = 0 end + local pos = self.object:getpos() + -- If we don't have a current action, we let the lua code choose one... if not self.action then @@ -333,14 +333,12 @@ minetest.register_entity("people:person" ,{ if err then dbg.v1("Lua error "..err) end if not self.action then self.action = {"wait", time=120} - dbg.v2(self.name.." failed to choose action - setting wait") + 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)) end end - local pos = self.object:getpos() - -- Handle gathering -- TODO: Only happens during go and follow currently. Can't happen -- during "dig" or any subaction that "dig" might set - which poses diff --git a/presets/FarmHand.lua b/presets/FarmHand.lua index 73df2a4..e3d0e40 100644 --- a/presets/FarmHand.lua +++ b/presets/FarmHand.lua @@ -37,15 +37,40 @@ if event.type == "program" then {"gather"}, + -- ******************** + -- 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"}, + {"gather"}, + -- ******************** -- Cactus Farm {"go", name="Cactus Farm"}, {"wait", time=10}, {"gather", topnodes={"default:cactus"}}, - {"go", pos={x=-40, y=11.5, z=-462}}, + {"go", pos={x=-34, y=11.5, z=-462}}, + {"go", pos={x=-34, y=11.5, z=-459}}, + {"go", pos={x=-40, y=11.5, z=-459}}, + {"go", pos={x=-40, y=11.5, z=-465}}, + {"go", pos={x=-33, y=11.5, z=-465}}, + {"go", pos={x=-34, y=11.5, z=-462}}, {"gather"}, {"go", name="Cactusville Square"}, - {"stash", items={"*default:cactus"}, dest="default:chest"}, + {"stash", items={"*default:cactus", + "*flowers:geranium", + "*flowers:rose", + "*flowers:dandelion_white", + "*flowers:dandelion_yellow", + "*flowers:viola", + "*flowers:tulip", + }, dest="default:chest"}, -- ***************** @@ -76,6 +101,7 @@ if event.type == "program" then "farming:seed_cotton 20", "farming_plus:carrot_seed 20", "farming_plus:tomato_seed 20", + "farming_plus:potato_seed 20", "farming_plus:strawberry_seed 20"}}, {"go", name="Ciaran's Farm"}, diff --git a/presets/RouteWalker.lua b/presets/RouteWalker.lua index 5f76588..eb042e0 100644 --- a/presets/RouteWalker.lua +++ b/presets/RouteWalker.lua @@ -1,28 +1,26 @@ if event.type == "program" then - if not mem.cur then + mem.positions = {pos} + mem.cur = 1 + mem.dir = 1 + mem.wait = true +elseif event.type == "tell" then + if event.message == "here" then + mem.positions = {event.senderpos} + mem.wait = false mem.cur = 1 + elseif event.message == "then here" then + table.insert(mem.positions, event.senderpos) end - if not mem.dir then - mem.dir = 1 - end - mem.positions = { - {x=182, y=6.5, z=214}, - "wait", - {x=163, y=6.5, z=213}, - "wait", - {x=163, y=6.5, z=194}, - "wait", - {x=181, y=6.5, z=194}, - } - elseif event.type == "act" then - if mem.positions[mem.cur] ~= "wait" then - action = {"go", pos=mem.positions[mem.cur]} - else + if mem.wait then action = {"wait", time=20} + mem.wait = false + return end + action = {"go", pos=mem.positions[mem.cur]} + mem.wait = true mem.cur = mem.cur + mem.dir if mem.cur > #mem.positions then mem.cur = #mem.positions diff --git a/tracking.lua b/tracking.lua index 1ecf089..6bf2608 100644 --- a/tracking.lua +++ b/tracking.lua @@ -120,7 +120,7 @@ people.people_wake = function() -- Wake the entity by (yuk) setting a forceload. The activation -- of the entity will cause the forceload to be freed. - dbg.v2("Set forceload for "..minetest.pos_to_string(v.pos)) + dbg.v2("Set forceload for "..minetest.pos_to_string(v.pos).." to activate "..k) minetest.forceload_block(v.pos) -- Only one at a time, even though this will introduce some