diff --git a/mob_meta.lua b/mob_meta.lua index 03d0f2c..e3b37e2 100644 --- a/mob_meta.lua +++ b/mob_meta.lua @@ -15,12 +15,12 @@ local cos = math.cos local atan2 = math.atan2 local function diff(a, b) -- Get difference between 2 angles - return math.atan2(math.sin(b - a), math.cos(b - a)) + return math.atan2(math.sin(b - a), math.cos(b - a)) end local function round(n, dec) - local x = 10^(dec or 0) - return math.floor(n * x + 0.5) / x + local x = 10^(dec or 0) + return math.floor(n * x + 0.5) / x end local vec_dir = vector.direction @@ -31,21 +31,21 @@ local vec_add = vector.add local vec_normal = vector.normalize local function vec_center(v) - return {x = floor(v.x + 0.5), y = floor(v.y + 0.5), z = floor(v.z + 0.5)} + return {x = floor(v.x + 0.5), y = floor(v.y + 0.5), z = floor(v.z + 0.5)} end local function vec_raise(v, n) - return {x = v.x, y = v.y + n, z = v.z} + return {x = v.x, y = v.y + n, z = v.z} end local function fast_ray_sight(pos1, pos2) - local ray = minetest.raycast(pos1, pos2, false, false) - for pointed_thing in ray do - if pointed_thing.type == "node" then - return false - end - end - return true + local ray = minetest.raycast(pos1, pos2, false, false) + for pointed_thing in ray do + if pointed_thing.type == "node" then + return false + end + end + return true end -- Local Utilities -- @@ -58,7 +58,7 @@ local function get_node_height(name) if def.walkable then if def.drawtype == "nodebox" then if def.node_box - and def.node_box.type == "fixed" then + and def.node_box.type == "fixed" then if type(def.node_box.fixed[1]) == "number" then return 0.5 + def.node_box.fixed[5] elseif type(def.node_box.fixed[1]) == "table" then @@ -78,52 +78,52 @@ local function get_node_height(name) end local function get_node_def(name) - local def = minetest.registered_nodes[name] or default_node_def - if def.walkable - and get_node_height(name) < 0.26 then - def.walkable = false -- workaround for nodes like snow - end - return def + local def = minetest.registered_nodes[name] or default_node_def + if def.walkable + and get_node_height(name) < 0.26 then + def.walkable = false -- workaround for nodes like snow + end + return def end local function get_ground_level(pos2, max_diff) - local node = minetest.get_node(pos2) - local node_under = minetest.get_node({ - x = pos2.x, - y = pos2.y - 1, - z = pos2.z - }) - local walkable = get_node_def(node_under.name) and not get_node_def(node.name) - if walkable then - return pos2 - end - local diff = 0 - if not get_node_def(node_under.name) then - for i = 1, max_diff do - pos2.y = pos2.y - 1 - node = minetest.get_node(pos2) - node_under = minetest.get_node({ - x = pos2.x, - y = pos2.y - 1, - z = pos2.z - }) - walkable = get_node_def(node_under.name) and not get_node_def(node.name) - if walkable then break end - end - else - for i = 1, max_diff do - pos2.y = pos2.y + 1 - node = minetest.get_node(pos2) - node_under = minetest.get_node({ - x = pos2.x, - y = pos2.y - 1, - z = pos2.z - }) - walkable = get_node_def(node_under.name) and not get_node_def(node.name) - if walkable then break end - end - end - return pos2 + local node = minetest.get_node(pos2) + local node_under = minetest.get_node({ + x = pos2.x, + y = pos2.y - 1, + z = pos2.z + }) + local walkable = get_node_def(node_under.name) and not get_node_def(node.name) + if walkable then + return pos2 + end + local diff = 0 + if not get_node_def(node_under.name) then + for i = 1, max_diff do + pos2.y = pos2.y - 1 + node = minetest.get_node(pos2) + node_under = minetest.get_node({ + x = pos2.x, + y = pos2.y - 1, + z = pos2.z + }) + walkable = get_node_def(node_under.name) and not get_node_def(node.name) + if walkable then break end + end + else + for i = 1, max_diff do + pos2.y = pos2.y + 1 + node = minetest.get_node(pos2) + node_under = minetest.get_node({ + x = pos2.x, + y = pos2.y - 1, + z = pos2.z + }) + walkable = get_node_def(node_under.name) and not get_node_def(node.name) + if walkable then break end + end + end + return pos2 end ------------------------- @@ -133,10 +133,10 @@ end local step_tick = 0.15 minetest.register_globalstep(function(dtime) - if step_tick <= 0 then - step_tick = 0.15 - end - step_tick = step_tick - dtime + if step_tick <= 0 then + step_tick = 0.15 + end + step_tick = step_tick - dtime end) -- A metatable is used to avoid issues @@ -144,15 +144,15 @@ end) -- their own scope local mob = { - -- Stats - max_health = 20, - armor_groups = {fleshy = 100}, - damage = 2, - speed = 4, + -- Stats + max_health = 20, + armor_groups = {fleshy = 100}, + damage = 2, + speed = 4, tracking_range = 16, - despawn_after = nil, - -- Physics - max_fall = 3, + despawn_after = nil, + -- Physics + max_fall = 3, stepheight = 1.1, hitbox = { width = 0.5, @@ -163,716 +163,718 @@ local mob = { local mob_meta = {__index = mob} local function index_box_border(self) - local width = self.width - local pos = self.object:get_pos() - pos.y = pos.y + 0.5 - local pos1 = { - x = pos.x - (width + 0.7), - y = pos.y, - z = pos.z - (width + 0.7), - } - local pos2 = { - x = pos.x + (width + 0.7), - y = pos.y, - z = pos.z + (width + 0.7), - } - local border = {} - for z = pos1.z, pos2.z do - for x = pos1.x, pos2.x do - local vec = { - x = x, - y = pos.y, - z = z - } - if not self:pos_in_box(vec, width) then - table.insert(border, vec_sub(vec, pos)) - end - end - end - return border + local width = self.width + local pos = self.object:get_pos() + pos.y = pos.y + 0.5 + local pos1 = { + x = pos.x - (width + 0.7), + y = pos.y, + z = pos.z - (width + 0.7), + } + local pos2 = { + x = pos.x + (width + 0.7), + y = pos.y, + z = pos.z + (width + 0.7), + } + local border = {} + for z = pos1.z, pos2.z do + for x = pos1.x, pos2.x do + local vec = { + x = x, + y = pos.y, + z = z + } + if not self:pos_in_box(vec, width) then + table.insert(border, vec_sub(vec, pos)) + end + end + end + return border end function mob:indicate_damage() - self._original_texture_mod = self._original_texture_mod or self.object:get_texture_mod() - self.object:set_texture_mod(self._original_texture_mod .. "^[colorize:#FF000040") - core.after(0.2, function() - if creatura.is_alive(self) then - self.object:set_texture_mod(self._original_texture_mod) - end - end) + self._original_texture_mod = self._original_texture_mod or self.object:get_texture_mod() + self.object:set_texture_mod(self._original_texture_mod .. "^[colorize:#FF000040") + core.after(0.2, function() + if creatura.is_alive(self) then + self.object:set_texture_mod(self._original_texture_mod) + end + end) end -- Set Movement Data function mob:move(pos, method, speed_factor, anim) - self._movement_data.goal = pos - self._movement_data.method = method - self._movement_data.last_neighbor = nil - self._movement_data.gravity = self._movement_data.gravity or -9.8 - self._movement_data.speed = (self.speed or 2) * (speed_factor or 1) - if anim then - self._movement_data.anim = anim - end + self._movement_data.goal = pos + self._movement_data.method = method + self._movement_data.last_neighbor = nil + self._movement_data.gravity = self._movement_data.gravity or -9.8 + self._movement_data.speed = (self.speed or 2) * (speed_factor or 1) + if anim then + self._movement_data.anim = anim + end end -- Clear Movement Data function mob:halt() - self._movement_data = { - goal = nil, - method = nil, - last_neighbor = nil, - gravity = self._movement_data.gravity or -9.8, - speed = 0 - } - self._path_data = {} + self._movement_data = { + goal = nil, + method = nil, + last_neighbor = nil, + gravity = self._movement_data.gravity or -9.8, + speed = 0 + } + self._path_data = {} end -- Turn to specified yaw function mob:turn_to(tyaw, rate) - self._tyaw = tyaw - local weight = rate or 10 - local yaw = self.object:get_yaw() + self._tyaw = tyaw + local weight = rate or 10 + local yaw = self.object:get_yaw() - yaw = yaw + pi - tyaw = (tyaw + pi) % pi2 + yaw = yaw + pi + tyaw = (tyaw + pi) % pi2 - local step = math.min(self.dtime * weight, abs(tyaw - yaw) % pi2) + local step = math.min(self.dtime * weight, abs(tyaw - yaw) % pi2) - local dir = abs(tyaw - yaw) > pi and -1 or 1 - dir = tyaw > yaw and dir * 1 or dir * -1 + local dir = abs(tyaw - yaw) > pi and -1 or 1 + dir = tyaw > yaw and dir * 1 or dir * -1 - local nyaw = (yaw + step * dir) % pi2 - self.object:set_yaw(nyaw - pi) - self.last_yaw = self.object:get_yaw() + local nyaw = (yaw + step * dir) % pi2 + self.object:set_yaw(nyaw - pi) + self.last_yaw = self.object:get_yaw() end -- Set Gravity (default of -9.8) function mob:set_gravity(gravity) - self._movement_data.gravity = gravity or -9.8 + self._movement_data.gravity = gravity or -9.8 end -- Sets Velocity to desired speed in mobs current look direction function mob:set_forward_velocity(speed) - local speed = speed or self._movement_data.speed - local dir = minetest.yaw_to_dir(self.object:get_yaw()) - local vel = vec_multi(dir, speed) - vel.y = self.object:get_velocity().y - self.object:set_velocity(vel) + local speed = speed or self._movement_data.speed + local dir = minetest.yaw_to_dir(self.object:get_yaw()) + local vel = vec_multi(dir, speed) + vel.y = self.object:get_velocity().y + self.object:set_velocity(vel) end -- Sets Velocity on y axis function mob:set_vertical_velocity(speed) - local vel = self.object:get_velocity() or {x = 0, y = 0, z = 0} - vel.y = speed - self.object:set_velocity(vel) + local vel = self.object:get_velocity() or {x = 0, y = 0, z = 0} + vel.y = speed + self.object:set_velocity(vel) end -- Applies knockback in 'dir' function mob:apply_knockback(dir, power) - if not dir then return end - power = power or 6 - if not self.touching_ground then - power = power * 0.8 - end - local knockback = vec_multi(dir, power) - knockback.y = abs(power * 0.22) - self.object:add_velocity(knockback) + if not dir then return end + power = power or 6 + if not self.touching_ground then + power = power * 0.8 + end + local knockback = vec_multi(dir, power) + knockback.y = abs(power * 0.22) + self.object:add_velocity(knockback) end -- Punch 'target' function mob:punch_target(target) -- - target:punch(self.object, 1.0, { - full_punch_interval = 1.0, - damage_groups = {fleshy = self.damage or 5}, - }) + target:punch(self.object, 1.0, { + full_punch_interval = 1.0, + damage_groups = {fleshy = self.damage or 5}, + }) end -- Apply damage to mob function mob:hurt(health) - if self.protected then return end - self.hp = self.hp - math.ceil(health) + if self.protected then return end + self.hp = self.hp - math.ceil(health) end -- Add HP to mob function mob:heal(health) - if self.protected then return end - self.hp = self.hp + math.ceil(health) - if self.hp > self.max_health then - self.hp = self.max_health - end + if self.protected then return end + self.hp = self.hp + math.ceil(health) + if self.hp > self.max_health then + self.hp = self.max_health + end end -- Return position at center of mobs hitbox function mob:get_center_pos() - return vec_raise(self.object:get_pos(), self.height * 0.5 or 0.5) + return vec_raise(self.object:get_pos(), self.height * 0.5 or 0.5) end -- Return true if position is within box function mob:pos_in_box(pos, size) - if not pos then return false end - local center = self:get_center_pos() - local width = size or self.width - local height = size or (self.height * 0.5) - if not size - and self.width < 0.5 then - width = 0.5 - end - local edge_a = { - x = center.x - width, - y = center.y - height, - z = center.z - width - } - local edge_b = { - x = center.x + width, - y = center.y + height, - z = center.z + width - } - local minp, maxp = vector.sort(edge_a, edge_b) - if pos.x >= minp.x - and pos.y >= minp.y - and pos.z >= minp.z - and pos.x <= maxp.x - and pos.y <= maxp.y - and pos.z <= maxp.z then - return true - end - return false + if not pos then return false end + local center = self:get_center_pos() + local width = size or self.width + local height = size or (self.height * 0.5) + if not size + and self.width < 0.5 then + width = 0.5 + end + local edge_a = { + x = center.x - width, + y = center.y - height, + z = center.z - width + } + local edge_b = { + x = center.x + width, + y = center.y + height, + z = center.z + width + } + local minp, maxp = vector.sort(edge_a, edge_b) + if pos.x >= minp.x + and pos.y >= minp.y + and pos.z >= minp.z + and pos.x <= maxp.x + and pos.y <= maxp.y + and pos.z <= maxp.z then + return true + end + return false end -- Terrain Navigation -- function mob:get_wander_pos(min_range, max_range, dir) - local pos = vec_center(self.object:get_pos()) - pos.y = floor(pos.y + 0.5) - local node = minetest.get_node(pos) - if get_node_def(node.name).walkable then -- Occurs if small mob is touching a fence - local offset = vector.add(pos, vec_multi(vec_dir(pos, self.object:get_pos()), 1.5)) - pos.x = floor(offset.x + 0.5) - pos.z = floor(offset.z + 0.5) - pos = get_ground_level(pos, 1) - end - local width = self.width - local outset = random(min_range, max_range) - if width < 0.6 then width = 0.6 end - local move_dir = vec_normal({ - x = random(-10, 10) * 0.1, - y = 0, - z = random(-10, 10) * 0.1 - }) - local pos2 = vec_add(pos, vec_multi(move_dir, width)) - if get_node_def(minetest.get_node(pos2).name).walkable - and not dir then - for i = 1, 3 do - move_dir = { - x = move_dir.z, - y = 0, - z = move_dir.x * -1 - } - pos2 = vec_add(pos, vec_multi(move_dir, width)) - if not get_node_def(minetest.get_node(pos2).name).walkable then - break - end - end - elseif dir then - move_dir = dir - end - for i = 1, outset do - local a_pos = vec_add(pos2, vec_multi(move_dir, i)) - local a_node = minetest.get_node(a_pos) - local b_pos = {x = a_pos.x, y = a_pos.y - 1, z = a_pos.z} - local b_node = minetest.get_node(b_pos) - if get_node_def(a_node.name).walkable - or not get_node_def(b_node.name).walkable then - a_pos = get_ground_level(a_pos, floor(self.stepheight or 1)) - end - if not get_node_def(a_node.name).walkable then - pos2 = a_pos - else - break - end - end - return pos2 + local pos = vec_center(self.object:get_pos()) + pos.y = floor(pos.y + 0.5) + local node = minetest.get_node(pos) + if get_node_def(node.name).walkable then -- Occurs if small mob is touching a fence + local offset = vector.add(pos, vec_multi(vec_dir(pos, self.object:get_pos()), 1.5)) + pos.x = floor(offset.x + 0.5) + pos.z = floor(offset.z + 0.5) + pos = get_ground_level(pos, 1) + end + local width = self.width + local outset = random(min_range, max_range) + if width < 0.6 then width = 0.6 end + local move_dir = vec_normal({ + x = random(-10, 10) * 0.1, + y = 0, + z = random(-10, 10) * 0.1 + }) + local pos2 = vec_add(pos, vec_multi(move_dir, width)) + if get_node_def(minetest.get_node(pos2).name).walkable + and not dir then + for i = 1, 3 do + move_dir = { + x = move_dir.z, + y = 0, + z = move_dir.x * -1 + } + pos2 = vec_add(pos, vec_multi(move_dir, width)) + if not get_node_def(minetest.get_node(pos2).name).walkable then + break + end + end + elseif dir then + move_dir = dir + end + for i = 1, outset do + local a_pos = vec_add(pos2, vec_multi(move_dir, i)) + local a_node = minetest.get_node(a_pos) + local b_pos = {x = a_pos.x, y = a_pos.y - 1, z = a_pos.z} + local b_node = minetest.get_node(b_pos) + if get_node_def(a_node.name).walkable + or not get_node_def(b_node.name).walkable then + a_pos = get_ground_level(a_pos, floor(self.stepheight or 1)) + end + if not get_node_def(a_node.name).walkable then + pos2 = a_pos + else + break + end + end + return pos2 end function mob:get_wander_pos_3d(min_range, max_range, dir, vert_bias) - local pos = vec_center(self.object:get_pos()) - local node = minetest.get_node(pos) - if get_node_def(node.name).walkable then -- Occurs if small mob is touching a fence - local offset = vector.add(pos, vec_multi(vec_dir(pos, self.object:get_pos()), 1.5)) - pos.x = floor(offset.x + 0.5) - pos.z = floor(offset.z + 0.5) - pos = get_ground_level(pos, 1) - end - local width = self.width - local outset = random(min_range, max_range) - if width < 0.6 then width = 0.6 end - local move_dir = vec_normal({ - x = random(-10, 10) * 0.1, - y = vert_bias or random(-10, 10) * 0.1, - z = random(-10, 10) * 0.1 - }) - local pos2 = vec_add(pos, vec_multi(move_dir, width)) - if get_node_def(minetest.get_node(pos2).name).walkable - and not dir then - for i = 1, 3 do - move_dir = { - x = move_dir.z, - y = move_dir.y, - z = move_dir.x * -1 - } - pos2 = vec_add(pos, vec_multi(move_dir, width)) - if not get_node_def(minetest.get_node(pos2).name).walkable then - break - end - end - elseif dir then - move_dir = dir - end - for i = 1, outset do - local a_pos = vec_add(pos2, vec_multi(move_dir, i)) - local a_node = minetest.get_node(a_pos) - if get_node_def(a_node.name).walkable then - a_pos = get_ground_level(a_pos, floor(self.stepheight or 1)) - end - if not get_node_def(a_node.name).walkable then - pos2 = a_pos - else - break - end - end - return pos2 + local pos = vec_center(self.object:get_pos()) + local node = minetest.get_node(pos) + if get_node_def(node.name).walkable then -- Occurs if small mob is touching a fence + local offset = vector.add(pos, vec_multi(vec_dir(pos, self.object:get_pos()), 1.5)) + pos.x = floor(offset.x + 0.5) + pos.z = floor(offset.z + 0.5) + pos = get_ground_level(pos, 1) + end + local width = self.width + local outset = random(min_range, max_range) + if width < 0.6 then width = 0.6 end + local move_dir = vec_normal({ + x = random(-10, 10) * 0.1, + y = vert_bias or random(-10, 10) * 0.1, + z = random(-10, 10) * 0.1 + }) + local pos2 = vec_add(pos, vec_multi(move_dir, width)) + if get_node_def(minetest.get_node(pos2).name).walkable + and not dir then + for i = 1, 3 do + move_dir = { + x = move_dir.z, + y = move_dir.y, + z = move_dir.x * -1 + } + pos2 = vec_add(pos, vec_multi(move_dir, width)) + if not get_node_def(minetest.get_node(pos2).name).walkable then + break + end + end + elseif dir then + move_dir = dir + end + for i = 1, outset do + local a_pos = vec_add(pos2, vec_multi(move_dir, i)) + local a_node = minetest.get_node(a_pos) + if get_node_def(a_node.name).walkable then + a_pos = get_ground_level(a_pos, floor(self.stepheight or 1)) + end + if not get_node_def(a_node.name).walkable then + pos2 = a_pos + else + break + end + end + return pos2 end function mob:is_pos_safe(pos) - local mob_pos = self.object:get_pos() - local node = minetest.get_node(pos) - if not node then return false end - if minetest.get_item_group(node.name, "igniter") > 0 - or get_node_def(node.name).drawtype == "liquid" - or get_node_def(minetest.get_node(vec_raise(pos, -1)).name).drawtype == "liquid" then return false end - local fall_safe = false - if self.max_fall ~= 0 then - for i = 1, self.max_fall or 3 do - local fall_pos = { - x = pos.x, - y = floor(mob_pos.y + 0.5) - i, - z = pos.z - } - if get_node_def(minetest.get_node(fall_pos).name).walkable then - fall_safe = true - break - end - end - else - fall_safe = true - end - return fall_safe + local mob_pos = self.object:get_pos() + local node = minetest.get_node(pos) + if not node then return false end + if minetest.get_item_group(node.name, "igniter") > 0 + or get_node_def(node.name).drawtype == "liquid" + or get_node_def(minetest.get_node(vec_raise(pos, -1)).name).drawtype == "liquid" then return false end + local fall_safe = false + if self.max_fall ~= 0 then + for i = 1, self.max_fall or 3 do + local fall_pos = { + x = pos.x, + y = floor(mob_pos.y + 0.5) - i, + z = pos.z + } + if get_node_def(minetest.get_node(fall_pos).name).walkable then + fall_safe = true + break + end + end + else + fall_safe = true + end + return fall_safe end -- Set mobs animation (if specified animation isn't already playing) function mob:animate(animation) - if not animation - or not self.animations[animation] then return end - if not self._anim - or self._anim ~= animation then - local anim = self.animations[animation] - self.object:set_animation(anim.range, anim.speed, anim.frame_blend, anim.loop) - self._anim = animation - end + if not animation + or not self.animations[animation] then return end + if not self._anim + or self._anim ~= animation then + local anim = self.animations[animation] + self.object:set_animation(anim.range, anim.speed, anim.frame_blend, anim.loop) + self._anim = animation + end end -- Set texture to variable at 'id' index in 'tbl' or 'textures' function mob:set_texture(id, tbl) - local _table = self.textures - if tbl then - _table = tbl - end - if not _table - or not _table[id] then - return - end - self.object:set_properties({ - textures = {_table[id]} - }) - return _table[id] + local _table = self.textures + if tbl then + _table = tbl + end + if not _table + or not _table[id] then + return + end + self.object:set_properties({ + textures = {_table[id]} + }) + return _table[id] end -- Set scale to base scale times 'x' and update bordering positions function mob:set_scale(x) - local def = minetest.registered_entities[self.name] - local scale = def.visual_size - local box = def.collisionbox - local new_box = {} - for k, v in ipairs(box) do - new_box[k] = v * x - end - self.object:set_properties({ - visual_size = { - x = scale.x * x, - y = scale.y * x - }, - collisionbox = new_box - }) - self._border = index_box_border(self) + local def = minetest.registered_entities[self.name] + local scale = def.visual_size + local box = def.collisionbox + local new_box = {} + for k, v in ipairs(box) do + new_box[k] = v * x + end + self.object:set_properties({ + visual_size = { + x = scale.x * x, + y = scale.y * x + }, + collisionbox = new_box + }) + self._border = index_box_border(self) end -- Fixes mob scale being changed when attached to a parent function mob:fix_attached_scale(parent) - local scale = self:get_visual_size() - local parent_size = parent:get_properties().visual_size - self.object:set_properties({ - visual_size = { - x = scale.x / parent_size.x, - y = scale.y / parent_size.y - }, - }) + local scale = self:get_visual_size() + local parent_size = parent:get_properties().visual_size + self.object:set_properties({ + visual_size = { + x = scale.x / parent_size.x, + y = scale.y / parent_size.y + }, + }) end -- Add sets 'id' to 'val' in permanent data function mob:memorize(id, val) - self.perm_data[id] = val - return self.perm_data[id] + self.perm_data[id] = val + return self.perm_data[id] end -- Remove 'id' from permanent data function mob:forget(id) - self.perm_data[id] = nil + self.perm_data[id] = nil end -- Return value from 'id' in permanent data function mob:recall(id) - return self.perm_data[id] + return self.perm_data[id] end -- Return true on interval specified by 'n' function mob:timer(n) - local t1 = floor(self.active_time) - local t2 = floor(self.active_time + self.dtime) - if t2 > t1 and t2%n == 0 then return true end + local t1 = floor(self.active_time) + local t2 = floor(self.active_time + self.dtime) + if t2 > t1 and t2%n == 0 then return true end end -- Play 'sound' from self.sounds function mob:play_sound(sound) - local spec = self.sounds and self.sounds[sound] - local parameters = {object = self.object} + local spec = self.sounds and self.sounds[sound] + local parameters = {object = self.object} - if type(spec) == "table" then - local name = spec.name - if spec.variations then - name = name .. "_" .. random(spec.variations) - end - local pitch = 1.0 + if type(spec) == "table" then + local name = spec.name + if spec.variations then + name = name .. "_" .. random(spec.variations) + end + local pitch = 1.0 - pitch = pitch - (random(-10, 10) * 0.005) + pitch = pitch - (random(-10, 10) * 0.005) - parameters.gain = spec.gain or 1 - parameters.max_hear_distance = spec.distance or 8 - parameters.fade = spec.fade or 1 - parameters.pitch = pitch - return minetest.sound_play(name, parameters) - end - return minetest.sound_play(spec, parameters) + parameters.gain = spec.gain or 1 + parameters.max_hear_distance = spec.distance or 8 + parameters.fade = spec.fade or 1 + parameters.pitch = pitch + return minetest.sound_play(name, parameters) + end + return minetest.sound_play(spec, parameters) end -- Return current collisionbox function mob:get_hitbox() - if not self.object:get_properties() then return self.collisionbox end - return self.object:get_properties().collisionbox + if not self.properties then return self.collisionbox end + return self.properties.collisionbox end -- Return height of current collisionbox function mob:get_height() - local hitbox = self:get_hitbox() - return hitbox[5] - hitbox[2] + local hitbox = self:get_hitbox() + return hitbox[5] - hitbox[2] end -- Return current visual size function mob:get_visual_size() - if not self.object:get_properties() then return end - return self.object:get_properties().visual_size + if not self.properties then return end + return self.properties.visual_size end local function is_group_in_table(tbl, name) - for _, v in pairs(tbl) do - if minetest.get_item_group(name, v:split(":")[2]) > 0 then - return true - end - end - return false + for _, v in pairs(tbl) do + if minetest.get_item_group(name, v:split(":")[2]) > 0 then + return true + end + end + return false end function mob:follow_wielded_item(player) - if not player - or not self.follow then return end - local item = player:get_wielded_item() - local name = item:get_name() - if type(self.follow) == "string" - and (name == self.follow - or minetest.get_item_group(name, self.follow:split(":")[2]) > 0) then - return item, name - end - if type(self.follow) == "table" - and (is_value_in_table(self.follow, name) - or is_group_in_table(self.follow, name)) then - return item, name - end + if not player + or not self.follow then return end + local item = player:get_wielded_item() + local name = item:get_name() + if type(self.follow) == "string" + and (name == self.follow + or minetest.get_item_group(name, self.follow:split(":")[2]) > 0) then + return item, name + end + if type(self.follow) == "table" + and (is_value_in_table(self.follow, name) + or is_group_in_table(self.follow, name)) then + return item, name + end end function mob:get_target(target) - local alive = creatura.is_alive(target) - if not alive then - return false, false, nil - end - if type(target) == "table" then - target = target.object - end - local pos = self:get_center_pos() - local tpos = target:get_pos() - tpos.y = floor(tpos.y + 0.5) - local line_of_sight = fast_ray_sight(pos, tpos) - return true, line_of_sight, tpos + local alive = creatura.is_alive(target) + if not alive then + return false, false, nil + end + if type(target) == "table" then + target = target.object + end + local pos = self:get_center_pos() + local tpos = target:get_pos() + tpos.y = floor(tpos.y + 0.5) + local line_of_sight = fast_ray_sight(pos, tpos) + return true, line_of_sight, tpos end -- Actions function mob:set_action(func) - self._action = func + self._action = func end function mob:get_action() - if type(self._action) ~= "table" then - return self._action - end - return nil + if type(self._action) ~= "table" then + return self._action + end + return nil end function mob:clear_action() - self._action = {} + self._action = {} end function mob:set_utility(func) - self._utility_data.func = func + self._utility_data.func = func end function mob:get_utility() - if not self._utility_data then return end - return self._utility_data.utility + if not self._utility_data then return end + return self._utility_data.utility end function mob:initiate_utility(utility, ...) - local func = creatura.registered_utilities[utility] - if not func then return end - self._utility_data.utility = utility - self:clear_action() - func(...) + local func = creatura.registered_utilities[utility] + if not func then return end + self._utility_data.utility = utility + self:clear_action() + func(...) end function mob:set_utility_score(n) - self._utility_data.score = n or 0 + self._utility_data.score = n or 0 end function mob:try_initiate_utility(utility, score, ...) if self._utility_data - and score >= self._utility_data.score then + and score >= self._utility_data.score then self:initiate_utility(utility, ...) self:set_utility_score(score) end end function mob:clear_utility() - self._utility_data = { - utility = nil, - func = nil, - score = 0 - } + self._utility_data = { + utility = nil, + func = nil, + score = 0 + } end -- Functions function mob:activate(staticdata, dtime) - self.width = self:get_hitbox()[4] or 0.5 - self.height = self:get_height() or 1 - self._tyaw = self.object:get_yaw() - self.last_yaw = self.object:get_yaw() - self.active_time = 0 - self.in_liquid = false - self.is_falling = false - self.touching_ground = false + self.properties = self.object:get_properties() + self.width = self:get_hitbox()[4] or 0.5 + self.height = self:get_height() or 1 + self._tyaw = self.object:get_yaw() + self.last_yaw = self.object:get_yaw() + self.active_time = 0 + self.in_liquid = false + self.is_falling = false + self.touching_ground = false - -- Backend Data (Should not be modified unless modder knows what they're doing) - self._movement_data = { - goal = nil, - method = nil, - last_neighbor = nil, - gravity = -9.8, - speed = 0 - } - self._path_data = {} - self._path = {} - self._task = {} - self._action = {} + -- Backend Data (Should not be modified unless modder knows what they're doing) + self._movement_data = { + goal = nil, + method = nil, + last_neighbor = nil, + gravity = -9.8, + speed = 0 + } + self._path_data = {} + self._path = {} + self._task = {} + self._action = {} - local pos = self.object:get_pos() - local node = minetest.get_node(pos) + local pos = self.object:get_pos() + local node = minetest.get_node(pos) - if node - and minetest.get_item_group(node.name, "liquid") > 0 then - self.in_liquid = node.name - end + if node + and minetest.get_item_group(node.name, "liquid") > 0 then + self.in_liquid = node.name + end - -- Staticdata - if staticdata then - local data = minetest.deserialize(staticdata) - if data then - for k, v in pairs(data) do - self[k] = v - end - end - end + -- Staticdata + if staticdata then + local data = minetest.deserialize(staticdata) + if data then + for k, v in pairs(data) do + self[k] = v + end + end + end - -- Initialize Stats and Visuals + -- Initialize Stats and Visuals if not self.textures then - local textures = self.object:get_properties().textures + local textures = self.properties.textures if textures then self.textures = textures end end - if not self.perm_data then - if self.memory then - self.perm_data = self.memory - else - self.perm_data = {} - end - if #self.textures > 1 then self.texture_no = random(#self.textures) end - end + if not self.perm_data then + if self.memory then + self.perm_data = self.memory + else + self.perm_data = {} + end + if #self.textures > 1 then self.texture_no = random(#self.textures) end + end - if self:recall("despawn_after") ~= nil then - self.despawn_after = self:recall("despawn_after") - end - self._despawn = self:recall("_despawn") or false + if self:recall("despawn_after") ~= nil then + self.despawn_after = self:recall("despawn_after") + end + self._despawn = self:recall("_despawn") or false - if self._despawn - and self.despawn_after then - self.object:remove() - return - end + if self._despawn + and self.despawn_after then + self.object:remove() + return + end - self._breath = self:recall("_breath") or (self.max_breath or 30) - self._border = index_box_border(self) + self._breath = self:recall("_breath") or (self.max_breath or 30) + self._border = index_box_border(self) if self.textures - and self.texture_no then + and self.texture_no then self:set_texture(self.texture_no, self.textures) end - self.max_health = self.max_health or 10 + self.max_health = self.max_health or 10 self.hp = self.hp or self.max_health - if type(self.armor_groups) ~= "table" then + if type(self.armor_groups) ~= "table" then self.armor_groups = {} end self.armor_groups.immortal = 1 self.object:set_armor_groups(self.armor_groups) - if self.timer - and type(self.timer) == "number" then -- fix crash for converted mobs_redo mobs - self.timer = function(self, n) - local t1 = floor(self.active_time) - local t2 = floor(self.active_time + self.dtime) - if t2 > t1 and t2%n == 0 then return true end - end - end + if self.timer + and type(self.timer) == "number" then -- fix crash for converted mobs_redo mobs + self.timer = function(self, n) + local t1 = floor(self.active_time) + local t2 = floor(self.active_time + self.dtime) + if t2 > t1 and t2%n == 0 then return true end + end + end - if self.activate_func then - self:activate_func(self, staticdata, dtime) - end + if self.activate_func then + self:activate_func(self, staticdata, dtime) + end end function mob:staticdata() - local data = {} - data.perm_data = self.perm_data - data.hp = self.hp or self.max_health - data.texture_no = self.texture_no or random(#self.textures) - return minetest.serialize(data) + local data = {} + data.perm_data = self.perm_data + data.hp = self.hp or self.max_health + data.texture_no = self.texture_no or random(#self.textures) + return minetest.serialize(data) end function mob:on_step(dtime, moveresult) - --local us_time = minetest.get_us_time() - if not self.hp then return end - self.dtime = dtime or 0.09 - self.moveresult = moveresult or {} - self.touching_ground = false - if moveresult then - self.touching_ground = moveresult.touching_ground - end - if step_tick <= 0 then - -- Physics and Vitals - if self._physics then - self:_physics(moveresult) - end - if self._vitals then - self:_vitals() - end - -- Cached Geometry - self.width = self:get_hitbox()[4] or 0.5 - self.height = self:get_height() or 1 - end - self:_light_physics() - -- Movement Control - if self._move then - self:_move() - end - if self.utility_stack - and self._execute_utilities then - self:_execute_utilities() - self:_execute_actions() - end - -- Die - if self.hp <= 0 - and self.death_func then - self:death_func() - self:halt() - return - end - if self.step_func - and self.perm_data then - self:step_func(dtime, moveresult) - end - self.active_time = self.active_time + dtime - if self.despawn_after - and self.active_time >= self.despawn_after then - self._despawn = self:memorize("_despawn", true) - end + --local us_time = minetest.get_us_time() + if not self.hp then return end + self.dtime = dtime or 0.09 + self.moveresult = moveresult or {} + self.touching_ground = false + if moveresult then + self.touching_ground = moveresult.touching_ground + end + if step_tick <= 0 then + -- Physics and Vitals + if self._physics then + self:_physics(moveresult) + end + if self._vitals then + self:_vitals() + end + -- 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 + self:_light_physics() + -- Movement Control + if self._move then + self:_move() + end + if self.utility_stack + and self._execute_utilities then + self:_execute_utilities() + self:_execute_actions() + end + -- Die + if self.hp <= 0 + and self.death_func then + self:death_func() + self:halt() + return + end + if self.step_func + and self.perm_data then + self:step_func(dtime, moveresult) + end + self.active_time = self.active_time + dtime + if self.despawn_after + and self.active_time >= self.despawn_after then + self._despawn = self:memorize("_despawn", true) + end end function mob:on_deactivate() - self._task = {} - self._action = {} - if self.deactivate_func then - self:deactivate_func(self) - end + self._task = {} + self._action = {} + if self.deactivate_func then + self:deactivate_func(self) + end end ---------------- @@ -892,159 +894,159 @@ end local moveable = creatura.is_pos_moveable local function do_step(self) - if not fancy_step then return end - local pos = self.object:get_pos() - local vel = self.object:get_velocity() - if not self._step then - if self.touching_ground - and abs(vel.x + vel.z) > 0 then - local border = self._border - local yaw_offset = vec_add(pos, vec_multi(minetest.yaw_to_dir(self.object:get_yaw()), self.width + 0.7)) - table.sort(border, function(a, b) return vec_dist(vec_add(pos, a), yaw_offset) < vec_dist(vec_add(pos, b), yaw_offset) end) - local step_pos = vec_center(vec_add(pos, border[1])) - local halfway = vec_add(pos, vec_multi(vec_dir(pos, step_pos), 0.5)) - halfway.y = step_pos.y - if creatura.get_node_def(step_pos).walkable - and abs(diff(self.object:get_yaw(), minetest.dir_to_yaw(vec_dir(pos, step_pos)))) < 1.5 - and moveable(halfway, self.width, self.height) then - self._step = vec_center(step_pos) - end - end - else - local vel = self.object:get_velocity() - self.object:set_velocity(vector.new(vel.x, 7, vel.z)) - if self._step.y < pos.y - 0.5 then - self.object:set_velocity(vector.new(vel.x, 0.5, vel.z)) - self._step = nil - local step_pos = self.object:get_pos() - local dir = minetest.yaw_to_dir(self.object:get_yaw()) - step_pos = vec_add(step_pos, vec_multi(dir, 0.1)) - self.object:set_pos(step_pos) - end - end + if not fancy_step then return end + local pos = self.object:get_pos() + local vel = self.object:get_velocity() + if not self._step then + if self.touching_ground + and abs(vel.x + vel.z) > 0 then + local border = self._border + local yaw_offset = vec_add(pos, vec_multi(minetest.yaw_to_dir(self.object:get_yaw()), self.width + 0.7)) + table.sort(border, function(a, b) return vec_dist(vec_add(pos, a), yaw_offset) < vec_dist(vec_add(pos, b), yaw_offset) end) + local step_pos = vec_center(vec_add(pos, border[1])) + local halfway = vec_add(pos, vec_multi(vec_dir(pos, step_pos), 0.5)) + halfway.y = step_pos.y + if creatura.get_node_def(step_pos).walkable + and abs(diff(self.object:get_yaw(), minetest.dir_to_yaw(vec_dir(pos, step_pos)))) < 1.5 + and moveable(halfway, self.width, self.height) then + self._step = vec_center(step_pos) + end + end + else + local vel = self.object:get_velocity() + self.object:set_velocity(vector.new(vel.x, 7, vel.z)) + if self._step.y < pos.y - 0.5 then + self.object:set_velocity(vector.new(vel.x, 0.5, vel.z)) + self._step = nil + local step_pos = self.object:get_pos() + local dir = minetest.yaw_to_dir(self.object:get_yaw()) + step_pos = vec_add(step_pos, vec_multi(dir, 0.1)) + self.object:set_pos(step_pos) + end + end end local function collision_detection(self) - if not creatura.is_alive(self) then return end - local pos = self.object:get_pos() - local width = self.width + 0.25 - local objects = minetest.get_objects_in_area(vec_sub(pos, width), vec_add(pos, width)) - if #objects < 2 then return end - local col_no = 0 - for i = 2, #objects do - local object = objects[i] - if creatura.is_alive(object) - and not self.object:get_attach() - and not object:get_attach() then - if i > 5 then break end - local pos2 = object:get_pos() - local dir = vec_dir(pos, pos2) - dir.y = 0 - if dir.x == 0 and dir.z == 0 then - dir = vector.new(random(-1, 1) * random(), 0, - random(-1, 1) * random()) - end - local velocity = vec_multi(dir, 1.1) - local vel1 = vec_multi(velocity, -2) -- multiplying by -2 accounts for friction - local vel2 = velocity - self.object:add_velocity(vel1) - object:add_velocity(vel2) - end - end + if not creatura.is_alive(self) then return end + local pos = self.object:get_pos() + local width = self.width + 0.25 + local objects = minetest.get_objects_in_area(vec_sub(pos, width), vec_add(pos, width)) + if #objects < 2 then return end + local col_no = 0 + for i = 2, #objects do + local object = objects[i] + if creatura.is_alive(object) + and not self.object:get_attach() + and not object:get_attach() then + if i > 5 then break end + local pos2 = object:get_pos() + local dir = vec_dir(pos, pos2) + dir.y = 0 + if dir.x == 0 and dir.z == 0 then + dir = vector.new(random(-1, 1) * random(), 0, + random(-1, 1) * random()) + end + local velocity = vec_multi(dir, 1.1) + local vel1 = vec_multi(velocity, -2) -- multiplying by -2 accounts for friction + local vel2 = velocity + self.object:add_velocity(vel1) + object:add_velocity(vel2) + end + end end local function water_physics(self) - -- 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) - local surface_node = minetest.get_node(surface_pos) - if minetest.get_item_group(floor_node.name, "liquid") < 1 then - self.object:set_acceleration({ - x = 0, - y = gravity, - z = 0 - }) - if self.in_liquid then - self.in_liquid = false - end - return - end - self.in_liquid = floor_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 - } - if minetest.get_item_group(minetest.get_node(step_pos).name, "liquid") > 0 then - surface_pos = step_pos - else - break - end - end - -- Apply Physics - local submergence = surface_pos.y - floor_pos.y - local vel = self.object:get_velocity() - local bouyancy = self.bouyancy_multiplier or 1 - self.object:set_acceleration({ - x = 0, - y = (submergence - vel.y * abs(vel.y) * 0.4) * bouyancy, - z = 0 - }) - local hydrodynamics = self.hydrodynamics_multiplier or 0.7 - local vel_y = vel.y - if self.bouyancy_multiplier == 0 then -- if bouyancy is disabled drag will be applied to keep awuatic mobs from drifting - vel_y = vel.y * hydrodynamics - end - self.object:set_velocity({ - x = vel.x * hydrodynamics, - y = vel_y, - z = vel.z * hydrodynamics - }) + -- 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) + local surface_node = minetest.get_node(surface_pos) + if minetest.get_item_group(floor_node.name, "liquid") < 1 then + self.object:set_acceleration({ + x = 0, + y = gravity, + z = 0 + }) + if self.in_liquid then + self.in_liquid = false + end + return + end + self.in_liquid = floor_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 + } + if minetest.get_item_group(minetest.get_node(step_pos).name, "liquid") > 0 then + surface_pos = step_pos + else + break + end + end + -- Apply Physics + local submergence = surface_pos.y - floor_pos.y + local vel = self.object:get_velocity() + local bouyancy = self.bouyancy_multiplier or 1 + self.object:set_acceleration({ + x = 0, + y = (submergence - vel.y * abs(vel.y) * 0.4) * bouyancy, + z = 0 + }) + local hydrodynamics = self.hydrodynamics_multiplier or 0.7 + local vel_y = vel.y + if self.bouyancy_multiplier == 0 then -- if bouyancy is disabled drag will be applied to keep awuatic mobs from drifting + vel_y = vel.y * hydrodynamics + end + self.object:set_velocity({ + x = vel.x * hydrodynamics, + y = vel_y, + z = vel.z * hydrodynamics + }) end function mob:_physics(moveresult) - if not self.object then return end - water_physics(self) - -- Step up nodes - do_step(self, moveresult) - -- Object collision - collision_detection(self) - if not self.in_liquid - and not self.touching_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 vel = self.object:get_velocity() - if self.touching_ground then - local nvel = vector.multiply(vel, 0.2) - if nvel.x < 0.2 - and nvel.z < 0.2 then - nvel.x = 0 - nvel.z = 0 - end - nvel.y = vel.y - self.object:set_velocity(nvel) - else - local nvel = vector.multiply(vel, 0.1) - if nvel.x < 0.2 - and nvel.z < 0.2 then - nvel.x = 0 - nvel.z = 0 - end - nvel.y = vel.y - self.object:set_velocity(nvel) - end - end + if not self.object then return end + water_physics(self) + -- Step up nodes + do_step(self, moveresult) + -- Object collision + collision_detection(self) + if not self.in_liquid + and not self.touching_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 vel = self.object:get_velocity() + if self.touching_ground then + local nvel = vector.multiply(vel, 0.2) + if nvel.x < 0.2 + and nvel.z < 0.2 then + nvel.x = 0 + nvel.z = 0 + end + nvel.y = vel.y + self.object:set_velocity(nvel) + else + local nvel = vector.multiply(vel, 0.1) + if nvel.x < 0.2 + and nvel.z < 0.2 then + nvel.x = 0 + nvel.z = 0 + end + nvel.y = vel.y + self.object:set_velocity(nvel) + end + end end function mob:_light_physics() -- physics that are lightweight enough to be called each step @@ -1053,216 +1055,216 @@ end -- Movement Control function mob:_move() - if not self.object then return end - local data = self._movement_data - local speed = data.speed - if data.goal then - local pos = data.goal - local method = data.method - local anim = data.anim - if creatura.registered_movement_methods[method] then - local func = creatura.registered_movement_methods[method] - func(self, pos, speed, anim) - end - end + if not self.object then return end + local data = self._movement_data + local speed = data.speed + if data.goal then + local pos = data.goal + local method = data.method + local anim = data.anim + if creatura.registered_movement_methods[method] then + local func = creatura.registered_movement_methods[method] + func(self, pos, speed, anim) + end + end end -- Execute Actions function mob:_execute_actions() - if not self.object then return end - local task = self._task - if #self._task > 0 then - local func = self._task[#self._task].func - if func(self) then - self._task[#self._task] = nil - self:clear_action() - return - end - end - local action = self._action - if type(action) ~= "table" then - local func = action - if func(self) then - self:clear_action() - end - end + if not self.object then return end + local task = self._task + if #self._task > 0 then + local func = self._task[#self._task].func + if func(self) then + self._task[#self._task] = nil + self:clear_action() + return + end + end + local action = self._action + if type(action) ~= "table" then + local func = action + if func(self) then + self:clear_action() + end + end end local function tbl_equals(tbl1, tbl2) - local match = true - for k, v in pairs(tbl1) do - if not tbl2[k] - and tbl2[k] ~= v then - match = false - break - end - end - return match + local match = true + for k, v in pairs(tbl1) do + if not tbl2[k] + and tbl2[k] ~= v then + match = false + break + end + end + return match end function mob:_execute_utilities() - local is_alive = self.hp > 0 - if not self._utility_data then - self._utility_data = { - utility = nil, - func = nil, - score = 0 - } - end - local loop_data = { - utility = nil, - func = nil, - score = 0 - } - if (self:timer(self.task_timer or 1) - or not self._utility_data.func) - and is_alive then - for i = 1, #self.utility_stack do - local utility = self.utility_stack[i].utility - local get_score = self.utility_stack[i].get_score - local score, args = get_score(self) - if self._utility_data.utility - and utility == self._utility_data.utility - and self._utility_data.score > 0 - and score <= 0 then - self._utility_data = { - utility = nil, - func = nil, - score = 0 - } - end - if score > 0 - and score >= self._utility_data.score - and score >= loop_data.score then - loop_data = { - utility = utility, - score = score, - args = args - } - end - end - end - if loop_data.utility - and loop_data.args then - local no_data = not self._utility_data.utility and not self._utility_data.args - local new_util = self._utility_data.utility ~= loop_data.utility or not tbl_equals(self._utility_data.args, loop_data.args) - if no_data - or new_util then -- if utilities are different or utilities are the same and args are different set new data - self._utility_data = loop_data - end - end - if self._utility_data.utility then - if not self._utility_data.func then - self:initiate_utility(self._utility_data.utility, unpack(self._utility_data.args)) - end - local func = self._utility_data.func - if not func then return end - if func(self) then - self._utility_data = { - utility = nil, - func = nil, - score = 0 - } - self:clear_action() - end - end + local is_alive = self.hp > 0 + if not self._utility_data then + self._utility_data = { + utility = nil, + func = nil, + score = 0 + } + end + local loop_data = { + utility = nil, + func = nil, + score = 0 + } + if (self:timer(self.task_timer or 1) + or not self._utility_data.func) + and is_alive then + for i = 1, #self.utility_stack do + local utility = self.utility_stack[i].utility + local get_score = self.utility_stack[i].get_score + local score, args = get_score(self) + if self._utility_data.utility + and utility == self._utility_data.utility + and self._utility_data.score > 0 + and score <= 0 then + self._utility_data = { + utility = nil, + func = nil, + score = 0 + } + end + if score > 0 + and score >= self._utility_data.score + and score >= loop_data.score then + loop_data = { + utility = utility, + score = score, + args = args + } + end + end + end + if loop_data.utility + and loop_data.args then + local no_data = not self._utility_data.utility and not self._utility_data.args + local new_util = self._utility_data.utility ~= loop_data.utility or not tbl_equals(self._utility_data.args, loop_data.args) + if no_data + or new_util then -- if utilities are different or utilities are the same and args are different set new data + self._utility_data = loop_data + end + end + if self._utility_data.utility then + if not self._utility_data.func then + self:initiate_utility(self._utility_data.utility, unpack(self._utility_data.args)) + end + local func = self._utility_data.func + if not func then return end + if func(self) then + self._utility_data = { + utility = nil, + func = nil, + score = 0 + } + self:clear_action() + end + end end -- Vitals function mob:_vitals() - local stand_pos = self.object:get_pos() - local fall_start = self._fall_start - if self.is_falling - and not fall_start - and self.max_fall > 0 then - self._fall_start = stand_pos.y - elseif fall_start - and self.max_fall > 0 then - if self.touching_ground - and not self.in_liquid then - local damage = fall_start - stand_pos.y - if damage < (self.max_fall or 3) then - self._fall_start = nil - return - end - local resist = self.fall_resistance or 0 - self:hurt(damage - (damage * (resist * 0.1))) - self:indicate_damage() - if random(4) < 2 then - self:play_sound("hurt") - end - self._fall_start = nil - elseif self.in_liquid then - self._fall_start = nil - end - end - if self:timer(1) then - local head_pos = vec_raise(stand_pos, self.height) - local head_def = get_node_def(minetest.get_node(head_pos).name) - if head_def.drawtype == "liquid" - and minetest.get_item_group(minetest.get_node(head_pos).name, "water") > 0 then - if self._breath <= 0 then - self:hurt(1) - self:indicate_damage() - if random(4) < 2 then - self:play_sound("hurt") - end - else - self._breath = self._breath - 1 - self:memorize("_breath", self._breath) - end - end - local stand_def = get_node_def(minetest.get_node(stand_pos).name) - if minetest.get_item_group(minetest.get_node(stand_pos).name, "fire") > 0 - and stand_def.damage_per_second then - local damage = stand_def.damage_per_second - local resist = self.fire_resistance or 0 - self:hurt(damage - (damage * (resist * 0.1))) - self:indicate_damage() - if random(4) < 2 then - self:play_sound("hurt") - end - end - end - if self:timer(5) then - local objects = minetest.get_objects_inside_radius(stand_pos, 0.2) - if #objects > 10 then - self:indicate_damage() - self.hp = self:memorize("hp", -1) - self:death_func() - end - end + local stand_pos = self.object:get_pos() + local fall_start = self._fall_start + if self.is_falling + and not fall_start + and self.max_fall > 0 then + self._fall_start = stand_pos.y + elseif fall_start + and self.max_fall > 0 then + if self.touching_ground + and not self.in_liquid then + local damage = fall_start - stand_pos.y + if damage < (self.max_fall or 3) then + self._fall_start = nil + return + end + local resist = self.fall_resistance or 0 + self:hurt(damage - (damage * (resist * 0.1))) + self:indicate_damage() + if random(4) < 2 then + self:play_sound("hurt") + end + self._fall_start = nil + elseif self.in_liquid then + self._fall_start = nil + end + end + if self:timer(1) then + local head_pos = vec_raise(stand_pos, self.height) + local head_def = get_node_def(minetest.get_node(head_pos).name) + if head_def.drawtype == "liquid" + and minetest.get_item_group(minetest.get_node(head_pos).name, "water") > 0 then + if self._breath <= 0 then + self:hurt(1) + self:indicate_damage() + if random(4) < 2 then + self:play_sound("hurt") + end + else + self._breath = self._breath - 1 + self:memorize("_breath", self._breath) + end + end + local stand_def = get_node_def(minetest.get_node(stand_pos).name) + if minetest.get_item_group(minetest.get_node(stand_pos).name, "fire") > 0 + and stand_def.damage_per_second then + local damage = stand_def.damage_per_second + local resist = self.fire_resistance or 0 + self:hurt(damage - (damage * (resist * 0.1))) + self:indicate_damage() + if random(4) < 2 then + self:play_sound("hurt") + end + end + end + if self:timer(5) then + local objects = minetest.get_objects_inside_radius(stand_pos, 0.2) + if #objects > 10 then + self:indicate_damage() + self.hp = self:memorize("hp", -1) + self:death_func() + end + end end function creatura.register_mob(name, def) - local box_width = def.hitbox and def.hitbox.width or 0.5 - local box_height = def.hitbox and def.hitbox.height or 1 - local hitbox = {-box_width, 0, -box_width, box_width, box_height, box_width} + local box_width = def.hitbox and def.hitbox.width or 0.5 + local box_height = def.hitbox and def.hitbox.height or 1 + local hitbox = {-box_width, 0, -box_width, box_width, box_height, box_width} - def.physical = def.physical or true - def.collide_with_objects = def.collide_with_objects or false - def.visual = "mesh" - def.makes_footstep_sound = def.makes_footstep_sound or false - if def.static_save ~= false then - def.static_save = true - end - def.collisionbox = hitbox - def._creatura_mob = true + def.physical = def.physical or true + def.collide_with_objects = def.collide_with_objects or false + def.visual = "mesh" + def.makes_footstep_sound = def.makes_footstep_sound or false + if def.static_save ~= false then + def.static_save = true + end + def.collisionbox = hitbox + def._creatura_mob = true - def.sounds = def.sounds or {} + def.sounds = def.sounds or {} - if not def.sounds.hit then - def.sounds.hit = { - name = "creatura_hit", - gain = 0.5, - distance = 16, + if not def.sounds.hit then + def.sounds.hit = { + name = "creatura_hit", + gain = 0.5, + distance = 16, variations = 3 - } - end + } + end - def.on_activate = function(self, staticdata, dtime) + def.on_activate = function(self, staticdata, dtime) return self:activate(staticdata, dtime) end @@ -1270,5 +1272,5 @@ function creatura.register_mob(name, def) return self:staticdata(self) end - minetest.register_entity(name, setmetatable(def, mob_meta)) + minetest.register_entity(name, setmetatable(def, mob_meta)) end