diff --git a/init.lua b/init.lua index efc1b59..1485ca4 100644 --- a/init.lua +++ b/init.lua @@ -82,19 +82,14 @@ local factors = {} -- Animation functions -- -local function animation_select(self, phase, segment) +local function animation_select(self, animation, segment) local state = self.entity_ai_state - state.phase = phase - local animname = self.script[state.driver].animations[phase] - if not animname then - print(self.name .. ": no animations for " .. phase .. "-" .. segment .. "(" .. animname ..")") - return - end - print("animation: " .. self.name .. ", phase = " .. phase .. ", anim = " .. animname .. ", " .. (segment or 0)) + state.animation = animation + print("animation: " .. self.name .. ", animation = " .. animation .. ", segment = " .. (segment or 0)) if not segment then - local animations = self.script.animations[animname] + local animations = self.script.animations[animation] if not animations then - print(self.name .. ": no animations for " .. phase .. "-" .. segment .. "(" .. animname ..")") + print(self.name .. ": no animations for " .. animation .. ", segment = " .. (segment or 0)) return end for i = 1, 3 do @@ -113,14 +108,14 @@ local function animation_select(self, phase, segment) end end else - local animdef = self.script.animations[animname][segment] + local animdef = self.script.animations[animation][segment] if animdef then state.segment = segment self.object:set_animation(animdef[1], animdef.frame_speed, animdef.frame_loop) return end end - print("animation_select: can't find animation " .. state.phase .. " for driver " .. state.driver .. " for entity " .. self.name) + print("animation_select: can't find animation " .. state.animation .. " for driver " .. state.driver .. " for entity " .. self.name) end local function animation_loop(self, dtime) @@ -132,7 +127,7 @@ local function animation_loop(self, dtime) state.animttl = nil state.factors.anim_end = true print("trigger anim_end") - animation_select(self, state.phase, state.segment + 1) + animation_select(self, state.animation, state.segment + 1) end end end @@ -156,111 +151,94 @@ end drivers.roam = { start = function(self) - -- start with moving animation + -- start with idle animation unless we get a path animation_select(self, "idle") local state = self.entity_ai_state - state.roam_idle = true state.roam_ttl = math.random(3, 9) + + -- get a target + local pos = self.object:getpos() + local minp, maxp = vector.sort({ + x = math.random(pos.x - 10, pos.x + 10), + y = pos.y - 5, + z = math.random(pos.z - 10, pos.z + 10) + }, { + x = math.random(pos.x - 10, pos.x + 10), + y = pos.y + 5, + z = math.random(pos.z - 10, pos.z + 10) + }) + minp, maxp = vector.sort(minp, maxp) + local nodes = minetest.find_nodes_in_area_under_air(minp, maxp, + {"group:flora", "group:snappy", "group:dirt", "group:soil", "group:crumbly", "default:dirt_with_dry_grass", "default:sand"}) + if #nodes == 0 then + -- failed to get a target + print("No target found, stopped") + state.driver = "idle" + drivers.idle.start(self) + return + end + + local pick = nodes[math.random(1, #nodes)] + -- find top walkable node + while true do + local node = minetest.get_node(pick) + if not minetest.registered_nodes[node.name].walkable then + pick.y = pick.y - 1 + else + -- one up at the end + pick.y = pick.y + 1 + break + end + end + -- move to the top surface of pick + if not pick then + print("no path found!") + state.driver = "idle" + drivers.idle.start(self) + return + end + + minetest.add_particle({ + pos = {x = pick.x, y = pick.y - 0.1, z = pick.z}, + velocity = vector.new(), + acceleration = vector.new(), + expirationtime = 3, + size = 6, + collisiondetection = false, + vertical = false, + texture = "wool_red.png", + playername = nil + }) + + self.path = Path(self, pick) + if not self.path:find() then + print("Unable to calculate path") + state.driver = "idle" + drivers.idle.start(self) + return + end + + -- done, roaming mode good! + animation_select(self, "move") end, step = function(self, dtime) animation_loop(self, dtime) consider_factors(self, dtime) -- handle movement stuff local state = self.entity_ai_state - if state.roam_ttl and state.roam_ttl > 0 then + if state.roam_ttl > 0 then state.roam_ttl = state.roam_ttl - dtime - if state.roam_idle then - -- we should already be stopped + -- do path movement + if not self.path or + self.path:distance() < 0.7 or + not self.path:step(dtime) then + state.driver = "idle" + drivers.idle.start(self) return - elseif state.roam_move then - -- do path movement - if not self.path then - state.roam_ttl = 0 - return - end - if self.path:distance() < 1.0 then - state.roam_ttl = 0 - return - end - if not self.path:step(dtime) then - -- pathing failed - state.roam_ttl = 0 - end - else - print("unknown roam state!") end else - -- reset ttl - state.roam_ttl = math.random(3, 9) - -- flip state - if state.roam_idle then - -- get a target - local pos = self.object:getpos() - local minp, maxp = vector.sort({ - x = math.random(pos.x - 10, pos.x + 10), - y = pos.y - 5, - z = math.random(pos.z - 10, pos.z + 10) - }, { - x = math.random(pos.x - 10, pos.x + 10), - y = pos.y + 5, - z = math.random(pos.z - 10, pos.z + 10) - }) - minp, maxp = vector.sort(minp, maxp) - local nodes = minetest.find_nodes_in_area_under_air(minp, maxp, - {"group:flora", "group:snappy", "group:dirt", "group:soil", "group:crumbly", "default:dirt_with_dry_grass", "default:sand"}) - if #nodes == 0 then - -- failed to get a target, just stand still - print("No target found, stopped") - return - end - - local pick = nodes[math.random(1, #nodes)] - -- find top walkable node - while true do - local node = minetest.get_node(pick) - if not minetest.registered_nodes[node.name].walkable then - pick.y = pick.y - 1 - else - -- one up at the end - pick.y = pick.y + 1 - break - end - end - -- move to the top surface of pick - if not pick then - print("no path found!") - return - end - - minetest.add_particle({ - pos = {x = pick.x, y = pick.y - 0.1, z = pick.z}, - velocity = vector.new(), - acceleration = vector.new(), - expirationtime = 3, - size = 6, - collisiondetection = false, - vertical = false, - texture = "wool_red.png", - playername = nil - }) - - self.path = Path(self, pick) - if not self.path:find() then - print("Unable to calculate path") - return - end - - -- done, roaming mode good! - animation_select(self, "move") - state.roam_idle = nil - state.roam_move = true - else - animation_select(self, "idle") - state.roam_idle = true - state.roam_move = nil - -- stop - self.object:setvelocity(vector.new()) - end + state.driver = "idle" + drivers.idle.start(self) end end, stop = function(self) @@ -268,6 +246,27 @@ drivers.roam = { end, } +drivers.idle = { + start = function(self) + animation_select(self, "idle") + self.object:setvelocity(vector.new()) + local state = self.entity_ai_state + state.idle_ttl = math.random(2, 20) + end, + step = function(self, dtime) + animation_loop(self, dtime) + consider_factors(self, dtime) + local state = self.entity_ai_state + state.idle_ttl = state.idle_ttl - dtime + if state.idle_ttl <= 0 then + state.driver = "roam" + drivers.roam.start(self) + end + end, + stop = function(self) + end, +} + drivers.startle = { start = function(self) -- startle animation @@ -292,7 +291,6 @@ drivers.startle = { drivers.eat = { start = function(self) - -- startle animation animation_select(self, "eat") self.object:setvelocity(vector.new()) -- collect info we want to use in this driver @@ -347,7 +345,7 @@ drivers.flee = { consider_factors(self, dtime) -- are we fleeing yet? - if self.path.distance then + if self.path and self.path.distance then -- stop fleeing if we're at a safe distance -- execute flee path if self.path:distance() < 2.0 then @@ -431,8 +429,6 @@ drivers.flee = { -- done, roaming mode good! animation_select(self, "move") - state.roam_idle = nil - state.roam_move = true end end, stop = function(self) @@ -609,6 +605,14 @@ local sheep_script = { -- mob script states: roam = { driver = "roaming", + factors = { + got_hit = "startle", + became_fertile = "fertile", + attractor_nearby = "attracted", + }, + }, + idle = { + driver = "idle", factors = { got_hit = "startle", became_fertile = "fertile", @@ -616,10 +620,6 @@ local sheep_script = { too_far_from_home = "homing", near_grass = "eat", }, - animations = { - move = "move", - idle = "idle", - }, }, eat = { driver = "eat", @@ -628,19 +628,12 @@ local sheep_script = { became_fertile = "fertile", attractor_nearby = "attracted", }, - animations = { - eat = "eat", - idle = "idle", - }, }, startle = { driver = "startle", factors = { anim_end = "flee", }, - animations = { - idle = "startle", - }, }, flee = { driver = "flee", @@ -648,9 +641,6 @@ local sheep_script = { got_hit = "startle", fleed_too_long = "roam", }, - animations = { - move = "run", - }, }, attracted = { driver = "approach", @@ -658,20 +648,12 @@ local sheep_script = { became_fertile = "fertile", approached_too_long = "roam", }, - animations = { - move = "move", - idle = "idle", - }, }, fertile = { driver = "mate", factors = { got_hit = "startle", }, - animations = { - move = "move", - idle = "idle", - }, }, homing = { driver = "homing", @@ -679,16 +661,9 @@ local sheep_script = { near_home = "roam", got_hit = "startle", }, - animations = { - move = "move", - idle = "idle", - }, }, death = { driver = "death", - animations = { - idle = "death", - }, }, } @@ -700,7 +675,7 @@ minetest.register_entity("entity_ai:sheep", { mesh = "sheep.b3d", textures = {"sheep_fur.png"}, -- standard stuff - collisionbox = {-7/16, -1/2, -7/16, 7/16, 6/16, 7/16}, + collisionbox = {-5/16, -1/2, -5/16, 5/16, 4/16, 5/16}, -- entity_ai stuff script = sheep_script, -- standard callbacks diff --git a/path.lua b/path.lua index 6edceaf..5e09f01 100644 --- a/path.lua +++ b/path.lua @@ -88,7 +88,7 @@ function Path:step(dtime) local curspd = self.object:getvelocity() local pos = self.object:getpos() -- if jumping, let jump finish before making more adjustments - if curspd.y <= 0.2 and curspd.y >= 0 then + if curspd.y >= 0 and curspd.y <= 2 then local i, v = next(self.path, nil) if not i then return false @@ -131,7 +131,7 @@ function Path:step(dtime) -- don't jump from too far away if vdif > 0.1 and len < 1.5 then -- jump - spd = {x = spd.x/10, y = 5, z = spd.z/10} + spd = {x = spd.x/4, y = 5, z = spd.z/4} self.object:setvelocity(spd) elseif vdif < 0 and len <= 1.1 then -- drop one path node just to be sure