diff --git a/Readme.md b/Readme.md index b292684..8432575 100644 --- a/Readme.md +++ b/Readme.md @@ -35,7 +35,7 @@ Inverse of `get_rotation`. ```lua moblib.register_entity(name, { - initial_properties = {...}, + initial_properties = {...}, lua_properties = { moveresult = { collisions = nil, diff --git a/main.lua b/main.lua index 9f41b7b..40c09b2 100644 --- a/main.lua +++ b/main.lua @@ -1,327 +1,327 @@ storage = minetest.get_mod_storage() entities_by_id = setmetatable({}, {__mode = "v"}) objects_by_id = setmetatable({}, { - __index = function(_, id) - local entity = entities_by_id[id] - return entity and entity.object - end, - __newindex = function(self, id, object) - local luaentity = object:get_luaentity() - if luaentity then - entities_by_id[id] = luaentity - else - self[id] = object - end - end, + __index = function(_, id) + local entity = entities_by_id[id] + return entity and entity.object + end, + __newindex = function(self, id, object) + local luaentity = object:get_luaentity() + if luaentity then + entities_by_id[id] = luaentity + else + self[id] = object + end + end, __mode = "v" }) local highest_id = storage:get_int("highest_id") minetest.register_on_joinplayer(function(player) - -- INFO no need to check whether it's an entity - rawset(objects_by_id, player:get_player_name(), player) + -- INFO no need to check whether it's an entity + rawset(objects_by_id, player:get_player_name(), player) end) minetest.register_on_leaveplayer(function(player) - objects_by_id[player:get_player_name()] = nil + objects_by_id[player:get_player_name()] = nil end) function get_id(object) - if object:is_player() then - return object:get_player_name() - end - local luaentity = object:get_luaentity() - if luaentity and luaentity._ then - return luaentity._.id - end + if object:is_player() then + return object:get_player_name() + end + local luaentity = object:get_luaentity() + if luaentity and luaentity._ then + return luaentity._.id + end end function get_object(id) - return objects_by_id[id] + return objects_by_id[id] end function get_entity(id) - return entities_by_id[id] + return entities_by_id[id] end -- x/z-rotation local function horizontal_rotation(direction) - return math.atan2(direction.y, math.sqrt(direction.x*direction.x + direction.z*direction.z)) + return math.atan2(direction.y, math.sqrt(direction.x*direction.x + direction.z*direction.z)) end -- y-rotation local function vertical_rotation(direction) - return -math.atan2(direction.x, direction.z) + return -math.atan2(direction.x, direction.z) end -- gets rotation in radians for a z-facing object function get_rotation(direction) - return { - x = horizontal_rotation(direction), - y = vertical_rotation(direction), - z = 0 - } + return { + x = horizontal_rotation(direction), + y = vertical_rotation(direction), + z = 0 + } end -- converts a rotation from -pi to pi to 2pi to 0 function convert_rotation(rotation) - return vector.apply(rotation, function(c) - if c < 0 then - return 2*math.pi + c - end - return c - end) + return vector.apply(rotation, function(c) + if c < 0 then + return 2*math.pi + c + end + return c + end) end -- shorthand function get_converted_rotation(direction) - return convert_rotation(get_rotation(direction)) + return convert_rotation(get_rotation(direction)) end -- normalizes a rotation function normalize_rotation(rotation) - return vector.apply(rotation, function(c) - local nc = c % (2*math.pi) - if c < 0 then - return 2*math.pi + nc - end - return nc - end) + return vector.apply(rotation, function(c) + local nc = c % (2*math.pi) + if c < 0 then + return 2*math.pi + nc + end + return nc + end) end function get_minimum_converted_rotation_difference(rotation, other_rotation) - return vector.apply(vector.subtract(rotation, other_rotation), function(c) - if c > math.pi then - return -2*math.pi + c - end - if c < -math.pi then - return 2*math.pi + c - end - return c - end) + return vector.apply(vector.subtract(rotation, other_rotation), function(c) + if c > math.pi then + return -2*math.pi + c + end + if c < -math.pi then + return 2*math.pi + c + end + return c + end) end -- gets rotation in radians for a wielditem (such as a sword) function get_wield_rotation(direction) - return { - x = 0, - y = 1.5*math.pi+vertical_rotation(direction), - z = 1.25*math.pi+horizontal_rotation(direction) - } + return { + x = 0, + y = 1.5*math.pi+vertical_rotation(direction), + z = 1.25*math.pi+horizontal_rotation(direction) + } end -- gets the direction for a rotated vector (0, 0, 1), inverse of get_rotation function get_direction(rotation) - local rx, ry = rotation.x, rotation.y - local direction = {} - -- x rotation - direction.y = math.sin(rx) - local z = math.cos(rx) - -- y rotation - direction.x = -(z * math.sin(ry)) - direction.z = z * math.cos(ry) - return direction + local rx, ry = rotation.x, rotation.y + local direction = {} + -- x rotation + direction.y = math.sin(rx) + local z = math.cos(rx) + -- y rotation + direction.x = -(z * math.sin(ry)) + direction.z = z * math.cos(ry) + return direction end function set_look_dir(player, direction) - local rotation = get_rotation(direction) - player:set_look_vertical(-rotation.x) - player:set_look_horizontal(rotation.y) + local rotation = get_rotation(direction) + player:set_look_vertical(-rotation.x) + player:set_look_horizontal(rotation.y) end function get_eye_pos(object) - local eye_pos = object:get_pos() - if object:is_player() then - eye_pos.y = eye_pos.y + object:get_properties().eye_height - end - return eye_pos + local eye_pos = object:get_pos() + if object:is_player() then + eye_pos.y = eye_pos.y + object:get_properties().eye_height + end + return eye_pos end function get_center(object) - local collisionbox = object:get_properties().collisionbox - return vector.add(object:get_pos(), vector.divide(vector.add(vector.new(collisionbox[1], collisionbox[2], collisionbox[3]), vector.new(unpack(collisionbox, 4))), 2)) + local collisionbox = object:get_properties().collisionbox + return vector.add(object:get_pos(), vector.divide(vector.add(vector.new(collisionbox[1], collisionbox[2], collisionbox[3]), vector.new(unpack(collisionbox, 4))), 2)) end function get_mass(object) - local entity = object:get_luaentity() - if entity and entity._mass then - return entity._mass - end - local collisionbox = object:get_properties().collisionbox - local mass = (collisionbox[4] - collisionbox[1]) * (collisionbox[5] - collisionbox[2]) * (collisionbox[6] - collisionbox[3]) - assert(mass > 0) - return mass + local entity = object:get_luaentity() + if entity and entity._mass then + return entity._mass + end + local collisionbox = object:get_properties().collisionbox + local mass = (collisionbox[4] - collisionbox[1]) * (collisionbox[5] - collisionbox[2]) * (collisionbox[6] - collisionbox[3]) + assert(mass > 0) + return mass end function calculate_damage(object, time_since_last_punch, caps) - local damage = 0 - local armor_groups = assert(object:get_armor_groups()) -- object has to be alive - for group, group_damage in pairs(caps.damage_groups) do - damage = damage + group_damage * (armor_groups[group] or 0) / 100 - end - return damage * math.min(1, math.max(0, time_since_last_punch / caps.full_punch_interval)) + local damage = 0 + local armor_groups = assert(object:get_armor_groups()) -- object has to be alive + for group, group_damage in pairs(caps.damage_groups) do + damage = damage + group_damage * (armor_groups[group] or 0) / 100 + end + return damage * math.min(1, math.max(0, time_since_last_punch / caps.full_punch_interval)) end -- TODO implement physics such as air resistance local engine_has_moveresult = minetest.has_feature("object_step_has_moveresult") local sensitivity = 0.01 function register_entity(name, def) - local props = def.lua_properties - def.lua_properties = nil - local on_activate = def.on_activate or function() end - local on_step = def.on_step or function() end - local terminal_speed = props.terminal_speed - if terminal_speed then - local old_on_step = on_step - function on_step(self, dtime, ...) - old_on_step(self, dtime, ...) - local obj = self.object - local vel = obj:get_velocity() - if not vel then return end -- object has been deleted - local len = vector.length(obj:get_velocity()) - if len > terminal_speed then - obj:set_velocity(vector.multiply(vector.divide(vel, len))) - end - end - end + local props = def.lua_properties + def.lua_properties = nil + local on_activate = def.on_activate or function() end + local on_step = def.on_step or function() end + local terminal_speed = props.terminal_speed + if terminal_speed then + local old_on_step = on_step + function on_step(self, dtime, ...) + old_on_step(self, dtime, ...) + local obj = self.object + local vel = obj:get_velocity() + if not vel then return end -- object has been deleted + local len = vector.length(obj:get_velocity()) + if len > terminal_speed then + obj:set_velocity(vector.multiply(vector.divide(vel, len))) + end + end + end local props_staticdata = props.staticdata if props_staticdata then - local implementation - if type(props_staticdata) == "table" then - implementation = props_staticdata - else - implementation = ({ - json = { - serializer = minetest.write_json, - deserializer = minetest.parse_json - }, - lua = { - serializer = minetest.serialize, - deserializer = minetest.deserialize - } - })[props_staticdata] - end - local serializer = implementation.serializer - local deserializer = implementation.deserializer - local old_on_activate = on_activate - function on_activate(self, staticdata, dtime) - self._ = (staticdata ~= "" and deserializer(staticdata)) or {} - old_on_activate(self, staticdata, dtime) - end - function def.get_staticdata(self) - return serializer(self._) - end - end - if props.id then - assert(props_staticdata) - local old_on_activate = on_activate - function on_activate(self, staticdata, dtime) - if not self._.id then - highest_id = highest_id + 1 - self._.id = highest_id - storage:set_int("highest_id", highest_id) - end - entities_by_id[self._.id] = self + local implementation + if type(props_staticdata) == "table" then + implementation = props_staticdata + else + implementation = ({ + json = { + serializer = minetest.write_json, + deserializer = minetest.parse_json + }, + lua = { + serializer = minetest.serialize, + deserializer = minetest.deserialize + } + })[props_staticdata] + end + local serializer = implementation.serializer + local deserializer = implementation.deserializer + local old_on_activate = on_activate + function on_activate(self, staticdata, dtime) + self._ = (staticdata ~= "" and deserializer(staticdata)) or {} old_on_activate(self, staticdata, dtime) - end - end - -- TODO consider HACK for #10158 - if props.moveresult then - -- localizing variables for performance reasons - local mr = props.moveresult - local mr_collisions = mr.collisions - local mr_axes = mr.axes - local mr_old_velocity = mr.old_velocity - local mr_acc_dependent = mr.acceleration_dependent - local mr_speed_diff = mr.speed_difference - local mr_moblib = mr.moblib - local old_on_activate = on_activate - function on_activate(self, staticdata, dtime) - old_on_activate(self, staticdata, dtime) - self._last_velocity = self.object:get_velocity() - end - local old_on_step = on_step - function on_step(self, dtime, moveresult) - local obj = self.object - if engine_has_moveresult and not mr_acc_dependent and not mr_moblib then - if moveresult.collides then - if mr_axes then - local axes = {} - for _, collision in ipairs(moveresult.collisions) do - axes[collision.axis] = true - end - moveresult.axes = axes - end - if mr_old_velocity then - if not moveresult.collisions[1] then - moveresult.old_velocity = self._last_velocity - else - moveresult.old_velocity = moveresult.collisions[1].old_velocity - end - end - if mr_speed_diff then - local expected_vel = vector.add(self._last_velocity, vector.multiply(obj:get_acceleration(), dtime)) - moveresult.speed_difference = vector.length(vector.subtract(expected_vel, obj:get_velocity())) - end - end - else - moveresult = {collides = false} - if self._last_velocity then - local expected_vel = vector.add(self._last_velocity, vector.multiply(obj:get_acceleration(), dtime)) - local velocity = obj:get_velocity() - local diff = vector.subtract(expected_vel, velocity) - local speed_difference = vector.length(diff) - local collides = speed_difference >= sensitivity - moveresult.collides = collides - if collides then - if mr_speed_diff then - moveresult.speed_difference = speed_difference - end - if mr_collisions then - local collisions = {} - diff = vector.apply(diff, math.abs) - local new_velocity = self._last_velocity - for axis, component_diff in pairs(diff) do - if component_diff > sensitivity then - new_velocity[axis] = velocity[axis] - table.insert(collisions, { - axis = axis, - old_velocity = self._last_velocity, - new_velocity = new_velocity - }) - end - end - moveresult.collisions = collisions - end - if mr_axes then - local axes = {} - diff = vector.apply(diff, math.abs) - for axis, component_diff in pairs(diff) do - if component_diff > sensitivity then - axes[axis] = true - end - end - moveresult.axes = axes - end - if mr_old_velocity then - moveresult.old_velocity = self._last_velocity - end - if mr_acc_dependent then - moveresult.acceleration_dependent = vector.length(vector.subtract(self._last_velocity, velocity)) < sensitivity - end - end - end - end - old_on_step(self, dtime, moveresult) - self._last_velocity = obj:get_velocity() - end - function def._set_velocity(self, velocity) - self.object:set_velocity(velocity) - self._last_velocity = velocity - end - end - def.on_activate = on_activate - def.on_step = on_step - minetest.register_entity(name, def) + end + function def.get_staticdata(self) + return serializer(self._) + end + end + if props.id then + assert(props_staticdata) + local old_on_activate = on_activate + function on_activate(self, staticdata, dtime) + if not self._.id then + highest_id = highest_id + 1 + self._.id = highest_id + storage:set_int("highest_id", highest_id) + end + entities_by_id[self._.id] = self + old_on_activate(self, staticdata, dtime) + end + end + -- TODO consider HACK for #10158 + if props.moveresult then + -- localizing variables for performance reasons + local mr = props.moveresult + local mr_collisions = mr.collisions + local mr_axes = mr.axes + local mr_old_velocity = mr.old_velocity + local mr_acc_dependent = mr.acceleration_dependent + local mr_speed_diff = mr.speed_difference + local mr_moblib = mr.moblib + local old_on_activate = on_activate + function on_activate(self, staticdata, dtime) + old_on_activate(self, staticdata, dtime) + self._last_velocity = self.object:get_velocity() + end + local old_on_step = on_step + function on_step(self, dtime, moveresult) + local obj = self.object + if engine_has_moveresult and not mr_acc_dependent and not mr_moblib then + if moveresult.collides then + if mr_axes then + local axes = {} + for _, collision in ipairs(moveresult.collisions) do + axes[collision.axis] = true + end + moveresult.axes = axes + end + if mr_old_velocity then + if not moveresult.collisions[1] then + moveresult.old_velocity = self._last_velocity + else + moveresult.old_velocity = moveresult.collisions[1].old_velocity + end + end + if mr_speed_diff then + local expected_vel = vector.add(self._last_velocity, vector.multiply(obj:get_acceleration(), dtime)) + moveresult.speed_difference = vector.length(vector.subtract(expected_vel, obj:get_velocity())) + end + end + else + moveresult = {collides = false} + if self._last_velocity then + local expected_vel = vector.add(self._last_velocity, vector.multiply(obj:get_acceleration(), dtime)) + local velocity = obj:get_velocity() + local diff = vector.subtract(expected_vel, velocity) + local speed_difference = vector.length(diff) + local collides = speed_difference >= sensitivity + moveresult.collides = collides + if collides then + if mr_speed_diff then + moveresult.speed_difference = speed_difference + end + if mr_collisions then + local collisions = {} + diff = vector.apply(diff, math.abs) + local new_velocity = self._last_velocity + for axis, component_diff in pairs(diff) do + if component_diff > sensitivity then + new_velocity[axis] = velocity[axis] + table.insert(collisions, { + axis = axis, + old_velocity = self._last_velocity, + new_velocity = new_velocity + }) + end + end + moveresult.collisions = collisions + end + if mr_axes then + local axes = {} + diff = vector.apply(diff, math.abs) + for axis, component_diff in pairs(diff) do + if component_diff > sensitivity then + axes[axis] = true + end + end + moveresult.axes = axes + end + if mr_old_velocity then + moveresult.old_velocity = self._last_velocity + end + if mr_acc_dependent then + moveresult.acceleration_dependent = vector.length(vector.subtract(self._last_velocity, velocity)) < sensitivity + end + end + end + end + old_on_step(self, dtime, moveresult) + self._last_velocity = obj:get_velocity() + end + function def._set_velocity(self, velocity) + self.object:set_velocity(velocity) + self._last_velocity = velocity + end + end + def.on_activate = on_activate + def.on_step = on_step + minetest.register_entity(name, def) end \ No newline at end of file