diff --git a/api.lua b/api.lua index 5f43e16..fcc4799 100644 --- a/api.lua +++ b/api.lua @@ -355,8 +355,13 @@ function creatura.sensor_ceil(self, range, water) return dist, node end +local get_objects = minetest.get_objects_inside_radius + function creatura.get_nearby_player(self, range) - local objects = minetest.get_objects_inside_radius(self:get_center_pos(), range or self.tracking_range) + local pos = self.object:get_pos() + if not pos then return end + local stored_objs = self._nearby_objs or {} + local objects = (#stored_objs > 0 and stored_objs) or get_objects(pos, range or self.tracking_range) for _, object in ipairs(objects) do if object:is_player() and creatura.is_alive(object) then @@ -366,7 +371,10 @@ function creatura.get_nearby_player(self, range) end function creatura.get_nearby_players(self, range) - local objects = minetest.get_objects_inside_radius(self:get_center_pos(), range or self.tracking_range) + local pos = self.object:get_pos() + if not pos then return end + local stored_objs = self._nearby_objs or {} + local objects = (#stored_objs > 0 and stored_objs) or get_objects(pos, range or self.tracking_range) local nearby = {} for _, object in ipairs(objects) do if object:is_player() @@ -378,7 +386,10 @@ function creatura.get_nearby_players(self, range) end function creatura.get_nearby_object(self, name, range) - local objects = minetest.get_objects_inside_radius(self:get_center_pos(), range or self.tracking_range) + local pos = self.object:get_pos() + if not pos then return end + local stored_objs = self._nearby_objs or {} + local objects = (#stored_objs > 0 and stored_objs) or get_objects(pos, range or self.tracking_range) for _, object in ipairs(objects) do local ent = creatura.is_alive(object) and object:get_luaentity() if ent @@ -392,7 +403,10 @@ function creatura.get_nearby_object(self, name, range) end function creatura.get_nearby_objects(self, name, range) - local objects = minetest.get_objects_inside_radius(self:get_center_pos(), range or self.tracking_range) + local pos = self.object:get_pos() + if not pos then return end + local stored_objs = self._nearby_objs or {} + local objects = (#stored_objs > 0 and stored_objs) or get_objects(pos, range or self.tracking_range) local nearby = {} for _, object in ipairs(objects) do local ent = creatura.is_alive(object) and object:get_luaentity() diff --git a/boids.lua b/boids.lua index 18d4cf9..1d47258 100644 --- a/boids.lua +++ b/boids.lua @@ -74,6 +74,14 @@ function creatura.get_boid_members(pos, radius, name) table.insert(members, object) end end + if #members > 1 then + for _, object in ipairs(members) do + local ent = object and object:get_luaentity() + if ent then + ent._movement_data.boids = members + end + end + end return members end diff --git a/methods.lua b/methods.lua index e04d460..8739ccb 100644 --- a/methods.lua +++ b/methods.lua @@ -32,15 +32,15 @@ local vec_add = vector.add local yaw2dir = minetest.yaw_to_dir local dir2yaw = minetest.dir_to_yaw ---[[local function debugpart(pos, time, tex) +local function debugpart(pos, time, tex) minetest.add_particle({ pos = pos, texture = tex or "creatura_particle_red.png", - expirationtime = time or 3, + expirationtime = time or 0.55, glow = 6, - size = 1 + size = 8 }) -end]] +end --------------------- -- Local Utilities -- @@ -101,7 +101,7 @@ end return false end]] -local function get_collision(self) +function creatura.get_collision(self) local yaw = self.object:get_yaw() local pos = self.object:get_pos() if not pos then return end @@ -113,41 +113,36 @@ local function get_collision(self) -- Loop local pos_x, pos_z = ahead.x, ahead.z for x = -width, width, width / ceil(width) do - local vec1 = { - x = cos(yaw) * ((pos_x + x) - pos_x) + pos_x, - y = pos.y + height, - z = sin(yaw) * ((pos_x + x) - pos_x) + pos_z - } - local vec2 = { - x = cos(yaw) * ((pos_x + x) - pos_x) + pos_x, - y = pos.y, - z = sin(yaw) * ((pos_x + x) - pos_x) + pos_z - } - local ray = raycast(vec1, vec2, false) - if ray then - local collision = ray.intersection_point - if collision.y - pos.y <= (self.stepheight or 1.1) then - local step = creatura.get_node_def({x = vec1.x, y = vec1.y + 0.5, z = vec1.z}) - if step.walkable then - return true, collision - end + for y = 0, height, height / ceil(height) do + local pos2 = { + x = cos(yaw) * ((pos_x + x) - pos_x) + pos_x, + y = pos.y + y, + z = sin(yaw) * ((pos_x + x) - pos_x) + pos_z + } + if pos2.y - pos.y > (self.stepheight or 1.1) + and creatura.get_node_def(pos2).walkable then + return true, pos2 end end end return false end +local get_collision = creatura.get_collision + local function get_avoidance_dir(self) local pos = self.object:get_pos() if not pos then return end local _, col_pos = get_collision(self) if col_pos then local vel = self.object:get_velocity() - local ahead = vec_add(pos, vec_normal(self.object:get_velocity())) + vel.y = 0 + local vel_len = vec_len(vel) * (1 + (self.step_delay or 0)) + local ahead = vec_add(pos, vec_normal(vel)) local avoidance_force = vector.subtract(ahead, col_pos) avoidance_force.y = 0 - local vel_len = vec_len(vel) avoidance_force = vec_multi(vec_normal(avoidance_force), (vel_len > 1 and vel_len) or 1) + debugpart(vec_add(ahead, avoidance_force)) return vec_dir(pos, vec_add(ahead, avoidance_force)) end end diff --git a/mob_meta.lua b/mob_meta.lua index dec18a7..1f0fa00 100644 --- a/mob_meta.lua +++ b/mob_meta.lua @@ -215,12 +215,12 @@ function mob:do_velocity() local vel = self.object:get_velocity() local yaw = self.object:get_yaw() if not yaw then return end - local dir = minetest.yaw_to_dir(yaw) local horz_vel = data.horz_vel local vert_vel = data.vert_vel - vel.x = (horz_vel and horz_vel * dir.x) or vel.x + if horz_vel and horz_vel < 1 then horz_vel = 1 end + vel.x = (horz_vel and (sin(yaw) * -horz_vel)) or vel.x vel.y = vert_vel or vel.y - vel.z = (horz_vel and horz_vel * dir.z) or vel.z + vel.z = (horz_vel and (cos(yaw) * horz_vel)) or vel.z self.object:set_velocity(vel) end @@ -629,6 +629,27 @@ function mob:get_target(target) return true, line_of_sight, tpos end +function mob:store_nearby_objects() + local pos = self.object:get_pos() + if not pos then return end + local objects = minetest.get_objects_inside_radius(pos, self.tracking_range or 8) + if #objects < 1 then return end + local objs = {} + for _, object in ipairs(objects) do + if creatura.is_alive(object) + and object ~= self.object then + local ent = object:get_luaentity() + local player = object:is_player() + if (ent + and not ent._ignore) + or player then + table.insert(objs, object) + end + end + end + self._nearby_objs = objs +end + -- Actions function mob:set_action(func) @@ -784,6 +805,8 @@ function mob:activate(staticdata, dtime) end end + self:store_nearby_objects() + if self.activate_func then self:activate_func(self, staticdata, dtime) end @@ -806,20 +829,27 @@ function mob:on_step(dtime, moveresult) if moveresult then self.touching_ground = moveresult.touching_ground end + local stand_pos + local stand_node if step_tick <= 0 then - -- Physics - if self._physics then - self:_physics(moveresult) - end - -- Vitals - if self._vitals then - self:_vitals() - end + stand_pos = self.object:get_pos() + if not stand_pos then return end + stand_node = minetest.get_node(stand_pos) -- Cached Geometry self.properties = self.object:get_properties() self.width = self:get_hitbox()[4] or 0.5 self.height = self:get_height() or 1 end + if stand_pos + and stand_node then + if self._vitals then + self:_vitals(stand_pos, stand_node) + end + if self._physics then + self:_physics(moveresult, stand_pos, stand_node) + end + end + if self:timer(10) then self:store_nearby_objects() end -- Reduce expensive calls self:do_velocity() self:do_turn() if self.utility_stack @@ -931,16 +961,14 @@ local function collision_detection(self) end end -local function water_physics(self) +local function water_physics(self, pos, node) -- Props local gravity = self._movement_data.gravity local height = self.height -- Vectors - local floor_pos = self.object:get_pos() - floor_pos.y = floor_pos.y + 0.01 - local surface_pos = floor_pos - local floor_node = minetest.get_node(floor_pos) - if minetest.get_item_group(floor_node.name, "liquid") < 1 then + pos.y = pos.y + 0.01 + local surface_pos = pos + if minetest.get_item_group(node.name, "liquid") < 1 then self.object:set_acceleration({ x = 0, y = gravity, @@ -951,13 +979,13 @@ local function water_physics(self) end return end - self.in_liquid = floor_node.name + self.in_liquid = node.name -- Get submergence (Not the most accurate, but reduces lag) for i = 1, math.ceil(height * 3) do local step_pos = { - x = floor_pos.x, - y = floor_pos.y + 0.5 * i, - z = floor_pos.z + x = pos.x, + y = pos.y + 0.5 * i, + z = pos.z } if minetest.get_item_group(minetest.get_node(step_pos).name, "liquid") > 0 then surface_pos = step_pos @@ -966,7 +994,7 @@ local function water_physics(self) end end -- Apply Physics - local submergence = surface_pos.y - floor_pos.y + local submergence = surface_pos.y - pos.y local vel = self.object:get_velocity() local bouyancy = self.bouyancy_multiplier or 1 local accel = (submergence - vel.y * abs(vel.y) * 0.4) * bouyancy @@ -988,23 +1016,27 @@ local function water_physics(self) }) end -function mob:_physics(moveresult) - if not self.object then return end - water_physics(self) +function mob:_physics(moveresult, pos, node) + if not pos then return end + water_physics(self, pos, node) -- Step up nodes do_step(self, moveresult) -- Object collision collision_detection(self) - if not self.in_liquid - and not self.touching_ground then + local in_liquid = self.in_liquid + local on_ground = self.touching_ground + if not in_liquid + and not on_ground then self.is_falling = true else self.is_falling = false end - if not self.in_liquid - and self._movement_data.gravity ~= 0 then + local move_data = self._movement_data + if not in_liquid + and not move_data.func + and move_data.gravity ~= 0 then local vel = self.object:get_velocity() - if self.touching_ground then + if on_ground then local nvel = vector.multiply(vel, 0.2) if nvel.x < 0.2 and nvel.z < 0.2 then @@ -1166,67 +1198,55 @@ end -- Vitals -function mob:_vitals() - local stand_pos = self.object:get_pos() - if not stand_pos then return end +function mob:_vitals(pos, node) + if not pos or not node then return end + local stand_def = creatura.get_node_def(node.name) local max_fall = self.max_fall or 0 - if max_fall > 0 then - local fall_start = self._fall_start - if self.is_falling - and not fall_start then - self._fall_start = stand_pos.y - elseif fall_start then - if self.touching_ground - and not self.in_liquid then - local damage = fall_start - stand_pos.y + local in_liquid = self.in_liquid + local on_ground = self.touching_ground + local damage + if max_fall > 0 + and not in_liquid then + local fall_start = self._fall_start or (not on_ground and pos.y) + if fall_start then + if on_ground then + damage = fall_start - pos.y if damage < max_fall then - self._fall_start = nil - return + damage = nil end local resist = self.fall_resistance or 0 - self:hurt(damage - (damage * resist)) - self:indicate_damage() - if random(4) < 2 then - self:play_sound("hurt") - end - self._fall_start = nil - elseif self.in_liquid then + damage = damage - damage * resist self._fall_start = nil end end end if self:timer(1) then - local head_pos = vec_raise(stand_pos, self.height) + local head_pos = vec_raise(pos, self.height) local head_node = minetest.get_node(head_pos) - local head_def = creatura.get_node_def(head_node.name) - if head_def.drawtype == "liquid" - and minetest.get_item_group(head_node.name, "water") > 0 then + if minetest.get_item_group(head_node.name, "liquid") > 0 then if self._breath <= 0 then - self:hurt(1) - self:indicate_damage() - if random(4) < 2 then - self:play_sound("hurt") - end + damage = (damage or 0) + 1 else self._breath = self._breath - 1 self:memorize("_breath", self._breath) end end - local stand_node = minetest.get_node(stand_pos) - local stand_def = creatura.get_node_def(stand_node.name) - if minetest.get_item_group(stand_node.name, "fire") > 0 + if minetest.get_item_group(stand_def.name, "fire") > 0 and stand_def.damage_per_second then - local damage = stand_def.damage_per_second local resist = self.fire_resistance or 0.5 - self:hurt(damage - damage * resist) - self:indicate_damage() - if random(4) < 2 then - self:play_sound("hurt") - end + damage = (damage or 0) + stand_def.damage_per_second * resist end end + if damage then + self:hurt(damage) + self:indicate_damage() + if random(4) < 2 then + self:play_sound("hurt") + end + end + -- Entity Cramming if self:timer(5) then - local objects = minetest.get_objects_inside_radius(stand_pos, 0.2) + local objects = minetest.get_objects_inside_radius(pos, 0.2) if #objects > 10 then self:indicate_damage() self.hp = self:memorize("hp", -1) diff --git a/pathfinder.lua b/pathfinder.lua index e2b466e..83d5ea8 100644 --- a/pathfinder.lua +++ b/pathfinder.lua @@ -134,9 +134,9 @@ function creatura.find_lvm_path(self, start, goal, obj_width, obj_height, max_op fly = fly or false swim = swim or false - start = start + if vec_dist(start, goal) > (self.tracking_range or 128) then return {} end - --self._path_data.start = start + self._path_data.start = start local path_neighbors = { {x = 1, y = 0, z = 0}, @@ -276,6 +276,12 @@ function creatura.find_lvm_path(self, start, goal, obj_width, obj_height, max_op self._path_data.open = openSet self._path_data.closedSet = closedSet + local current_start = vec_round(self._path_data.start) + + if closedSet[minetest.hash_node_position(current_start)] then + start_index = minetest.hash_node_position(current_start) + end + -- Reconstruct path if end is reached if ((is_on_ground(_goal) or fly)