From 84202237f85e0124ea2f5def71a1f50fc538d491 Mon Sep 17 00:00:00 2001 From: Alexander Minges Date: Fri, 1 May 2020 14:56:29 +0200 Subject: [PATCH] attempt to fix mobs trapped in water check for possible destinations by analyzing a whole set of nodes; check if air is above those nodes refactor code; adjust checks for dangerous blocks performance optimization, remove iteration when looking for safe node take jump height into account; look for water instead of solid for swimming mobs remove debug logging --- mods/ENTITIES/mcl_mobs/api.lua | 117 ++++++++++++++++++++++----------- 1 file changed, 78 insertions(+), 39 deletions(-) diff --git a/mods/ENTITIES/mcl_mobs/api.lua b/mods/ENTITIES/mcl_mobs/api.lua index 4a4a56bb..cf3913af 100644 --- a/mods/ENTITIES/mcl_mobs/api.lua +++ b/mods/ENTITIES/mcl_mobs/api.lua @@ -300,6 +300,11 @@ end -- Returns true is node can deal damage to self local is_node_dangerous = function(self, nodename) local nn = nodename + if self.fly then + if not minetest.get_item_group(nn, self.fly_in) then + return true + end + end if self.water_damage > 0 then if minetest.get_item_group(nn, "water") ~= 0 then return true @@ -317,7 +322,10 @@ local is_node_dangerous = function(self, nodename) end if minetest.registered_nodes[nn].drowning > 0 then if self.breath_max ~= -1 then - return true + -- check if the mob is water-breathing _and_ the block is water; only return true if neither is the case + if not self.breathes_in_water and minetest.get_item_group(nn, "water") ~= 0 then + return true + end end end if minetest.registered_nodes[nn].damage_per_second > 0 then @@ -2013,8 +2021,12 @@ local do_states = function(self, dtime) if (self.water_damage > 0 and self.lava_damage > 0) or self.breath_max ~= -1 then - - lp = minetest.find_node_near(s, 1, {"group:water", "group:lava"}) + -- water-breathing mobs don't have to avoid water, but air + if self.breathes_in_water then + lp = minetest.find_node_near(s, 1, {"air", "group:lava"}) + else + lp = minetest.find_node_near(s, 1, {"group:water", "group:lava"}) + end elseif self.water_damage > 0 then @@ -2032,49 +2044,76 @@ local do_states = function(self, dtime) local is_in_danger = false if lp then - -- If mob in or on dangerous block, look for land - if (is_node_dangerous(self, self.standing_in) or - is_node_dangerous(self, self.standing_on)) then + -- if mob is flying, only check the block it is inside + if self.fly then + if is_node_dangerous(self, self.standing_in) then + is_in_danger = true + end + elseif is_node_dangerous(self, self.standing_in) or is_node_dangerous(self, self.standing_on) then is_in_danger = true - - lp = minetest.find_node_near(s, 5, {"group:solid"}) + end + -- If mob in or on dangerous block, look for land + if is_in_danger then + local tab_lp = nil + if self.breathes_in_water and self.fly then + tab_lp = minetest.find_nodes_in_area({x=s.x-1, y=s.y-1, z=s.z-1}, {x=s.x+1, y=s.y+1, z=s.z+1}, {"group:water"}) + elseif minetest.get_item_group(self.standing_in, "water") then + tab_lp = minetest.find_nodes_in_area_under_air({x=s.x-1, y=s.y-self.fear_height, z=s.z-1}, {x=s.x+1, y=s.y-1, z=s.z+1}, {"group:solid"}) + elseif not self.jump then + tab_lp = minetest.find_nodes_in_area_under_air({x=s.x-1, y=s.y-1, z=s.z-1}, {x=s.x+1, y=s.y-1, z=s.z+1}, {"group:solid"}) + else + tab_lp = minetest.find_nodes_in_area_under_air({x=s.x-1, y=s.y-self.fear_height, z=s.z-1}, {x=s.x+1, y=s.y+(self.jump_height - 1), z=s.z+1}, {"group:solid"}) + end -- did we find land? - if lp then - local nn = minetest.get_node(lp).name - if is_node_dangerous(self, nn) then - -- is the chosen destination safe? Retry if not (max 10 iterations) - local i = 0 - while i < 10 do - if lp then - nn = minetest.get_node(lp).name - minetest.log("Found solid block: " .. nn) - if not is_node_dangerous(self, nn) then break + if #tab_lp >= 1 then + for index, lp in ipairs(tab_lp) do + + local nn = minetest.get_node(lp).name + local node_above_ok = false + + local y_difference = lp.y - s.y + local y_difference_ok = false + + if y_difference <= 0 then + y_difference_ok = true + elseif (y_difference <= self.jump_height) and not minetest.get_item_group(nn, "water") then + y_difference_ok = true + end + + -- is the chosen destination safe and walkable? + if not is_node_dangerous(self, nn) and minetest.registered_nodes[nn].walkable and y_difference_ok then + -- check node at y + 2 above + local lp_above = {x = lp.x, y = lp.y + 1, z = lp.z} + local nn_above = minetest.get_node(lp_above).name + if self.breath_max ~= -1 and self.breathes_in_water and minetest.get_item_group(nn_above, "water") then + -- for water-breathing mobs water is accepted + node_above_ok = true + elseif nn_above == "air" then + -- in any other case there should be air, so the block can be stepped on + node_above_ok = true end - -- look for solid node 5 blocks around the mob's position - elseif i < 5 then - lp = minetest.find_node_near(s, 5, {"group:solid"}) - -- after 5 iterations double search radius - elseif i < 10 then - lp = minetest.find_node_near(s, 10, {"group:solid"}) + + if node_above_ok then + local vec = { + x = lp.x - s.x, + z = lp.z - s.z + } + + yaw = (atan(vec.z / vec.x) + pi / 2) - self.rotate + + if lp.x > s.x then yaw = yaw + pi end + + -- look towards land and jump/move in that direction + yaw = set_yaw(self, yaw, 6) + do_jump(self) + set_velocity(self, self.walk_velocity) + break + else + yaw = yaw + random(-0.5, 0.5) end - i = i + 1 end end - - local vec = { - x = lp.x - s.x, - z = lp.z - s.z - } - - yaw = (atan(vec.z / vec.x) + pi / 2) - self.rotate - - if lp.x > s.x then yaw = yaw + pi end - - -- look towards land and jump/move in that direction - yaw = set_yaw(self, yaw, 6) - do_jump(self) - set_velocity(self, self.walk_velocity) else yaw = yaw + random(-0.5, 0.5) end