diff --git a/mods/CORE/mcl_compatibility/init.lua b/mods/CORE/mcl_compatibility/init.lua new file mode 100644 index 000000000..1ca6fc5ef --- /dev/null +++ b/mods/CORE/mcl_compatibility/init.lua @@ -0,0 +1,92 @@ +mcl_compatibility = mcl_compatibility or {} +mcl_vars = mcl_vars or {} + +local modname = minetest.get_current_modname() +local modpath = minetest.get_modpath(modname) + +function math.round(x) + if x >= 0 then + return math.floor(x + 0.5) + end + return math.ceil(x - 0.5) +end + +dofile(modpath .. "/vector.lua") + +local minetest_get_node = minetest.get_node + +mcl_compatibility.sort_nodes = function(nodes) + if not nodes then return {} end + for _, pos in pairs(nodes) do + if not pos.x or not pos.y or not pos.z then + return nodes + end + end + local new_nodes = {} + for _, pos in pairs(nodes) do + local node = minetest_get_node(pos) + local name = node.name + if not new_nodes[name] then + new_nodes[name] = { pos } + else + table.insert(new_nodes[name], pos) + end + end + return new_nodes +end + +local minetest_find_nodes_in_area = minetest.find_nodes_in_area +minetest.find_nodes_in_area = function(pos1, pos2, nodenames, grouped) + local nodes, num = minetest_find_nodes_in_area(pos1, pos2, nodenames, grouped) + if not grouped or not nodes or next(nodes) == nil then + return nodes, num + end + return mcl_compatibility.sort_nodes(nodes) +end + +function mcl_vars.pos_to_block(pos) + return mcl_mapgen and mcl_mapgen.pos_to_block(pos) +end + +function mcl_vars.pos_to_chunk(pos) + return mcl_mapgen and mcl_mapgen.pos_to_chunk(pos) +end + +function mcl_vars.get_chunk_number(pos) + return mcl_mapgen and get_chunk_number(pos) +end + +function mcl_vars.is_generated(pos) + local node = minetest.get_node(pos) + if not node then return false end + if node.name == "ignore" then return false end + return true +end + +function mcl_vars.get_node(p, force, us_timeout) + if not p or not p.x or not p.y or not p.z then return {name="error"} end + local node = minetest.get_node(p) + if node.name ~= "ignore" then return node end + minetest.get_voxel_manip():read_from_map(p, p) + return minetest.get_node(pos) +end + +mcl_vars.mg_overworld_min = -62 +mcl_vars.mg_overworld_max_official = 198 +mcl_vars.mg_bedrock_overworld_min = -62 +mcl_vars.mg_bedrock_overworld_max = -58 +mcl_vars.mg_lava_overworld_max = -52 +mcl_vars.mg_lava = true +mcl_vars.mg_bedrock_is_rough = true +mcl_vars.mg_overworld_max = 30927 +mcl_vars.mg_nether_min = -29067 +mcl_vars.mg_nether_max = -28939 +mcl_vars.mg_bedrock_nether_bottom_min = -29067 +mcl_vars.mg_bedrock_nether_top_max = -29063 +mcl_vars.mg_end_min = -27073 +mcl_vars.mg_end_max_official = -26817 +mcl_vars.mg_end_max = -2062 +mcl_vars.mg_end_platform_pos = { x = 100, y = -26999, z = 0 } +mcl_vars.mg_realm_barrier_overworld_end_max = -2062 +mcl_vars.mg_realm_barrier_overworld_end_min = -2073 +mcl_vars.mg_dungeons = true diff --git a/mods/CORE/mcl_compatibility/mod.conf b/mods/CORE/mcl_compatibility/mod.conf new file mode 100644 index 000000000..b56060ca4 --- /dev/null +++ b/mods/CORE/mcl_compatibility/mod.conf @@ -0,0 +1,3 @@ +name = mcl_compatibility +author = kay27, minetest devs +description = Makes MineClone 5 run in old versions of Minetest and be compatible with mods using old MineClone APIs diff --git a/mods/CORE/mcl_compatibility/vector.lua b/mods/CORE/mcl_compatibility/vector.lua new file mode 100644 index 000000000..581d014e0 --- /dev/null +++ b/mods/CORE/mcl_compatibility/vector.lua @@ -0,0 +1,362 @@ +--[[ +Vector helpers +Note: The vector.*-functions must be able to accept old vectors that had no metatables +]] + +-- localize functions +local setmetatable = setmetatable + +vector = {} + +local metatable = {} +vector.metatable = metatable + +local xyz = {"x", "y", "z"} + +-- only called when rawget(v, key) returns nil +function metatable.__index(v, key) + return rawget(v, xyz[key]) or vector[key] +end + +-- only called when rawget(v, key) returns nil +function metatable.__newindex(v, key, value) + rawset(v, xyz[key] or key, value) +end + +-- constructors + +local function fast_new(x, y, z) + return setmetatable({x = x, y = y, z = z}, metatable) +end + +function vector.new(a, b, c) + if a and b and c then + return fast_new(a, b, c) + end + + -- deprecated, use vector.copy and vector.zero directly + if type(a) == "table" then + return vector.copy(a) + else + assert(not a, "Invalid arguments for vector.new()") + return vector.zero() + end +end + +function vector.zero() + return fast_new(0, 0, 0) +end + +function vector.copy(v) + assert(v.x and v.y and v.z, "Invalid vector passed to vector.copy()") + return fast_new(v.x, v.y, v.z) +end + +function vector.from_string(s, init) + local x, y, z, np = string.match(s, "^%s*%(%s*([^%s,]+)%s*[,%s]%s*([^%s,]+)%s*[,%s]" .. + "%s*([^%s,]+)%s*[,%s]?%s*%)()", init) + x = tonumber(x) + y = tonumber(y) + z = tonumber(z) + if not (x and y and z) then + return nil + end + return fast_new(x, y, z), np +end + +function vector.to_string(v) + return string.format("(%g, %g, %g)", v.x, v.y, v.z) +end +metatable.__tostring = vector.to_string + +function vector.equals(a, b) + return a.x == b.x and + a.y == b.y and + a.z == b.z +end +metatable.__eq = vector.equals + +-- unary operations + +function vector.length(v) + return math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z) +end +-- Note: we can not use __len because it is already used for primitive table length + +function vector.normalize(v) + local len = vector.length(v) + if len == 0 then + return fast_new(0, 0, 0) + else + return vector.divide(v, len) + end +end + +function vector.floor(v) + return vector.apply(v, math.floor) +end + +function vector.round(v) + return fast_new( + math.round(v.x), + math.round(v.y), + math.round(v.z) + ) +end + +function vector.apply(v, func) + return fast_new( + func(v.x), + func(v.y), + func(v.z) + ) +end + +function vector.distance(a, b) + local x = a.x - b.x + local y = a.y - b.y + local z = a.z - b.z + return math.sqrt(x * x + y * y + z * z) +end + +function vector.direction(pos1, pos2) + return vector.subtract(pos2, pos1):normalize() +end + +function vector.angle(a, b) + local dotp = vector.dot(a, b) + local cp = vector.cross(a, b) + local crossplen = vector.length(cp) + return math.atan2(crossplen, dotp) +end + +function vector.dot(a, b) + return a.x * b.x + a.y * b.y + a.z * b.z +end + +function vector.cross(a, b) + return fast_new( + a.y * b.z - a.z * b.y, + a.z * b.x - a.x * b.z, + a.x * b.y - a.y * b.x + ) +end + +function metatable.__unm(v) + return fast_new(-v.x, -v.y, -v.z) +end + +-- add, sub, mul, div operations + +function vector.add(a, b) + if type(b) == "table" then + return fast_new( + a.x + b.x, + a.y + b.y, + a.z + b.z + ) + else + return fast_new( + a.x + b, + a.y + b, + a.z + b + ) + end +end +function metatable.__add(a, b) + return fast_new( + a.x + b.x, + a.y + b.y, + a.z + b.z + ) +end + +function vector.subtract(a, b) + if type(b) == "table" then + return fast_new( + a.x - b.x, + a.y - b.y, + a.z - b.z + ) + else + return fast_new( + a.x - b, + a.y - b, + a.z - b + ) + end +end +function metatable.__sub(a, b) + return fast_new( + a.x - b.x, + a.y - b.y, + a.z - b.z + ) +end + +function vector.multiply(a, b) + if type(b) == "table" then + return fast_new( + a.x * b.x, + a.y * b.y, + a.z * b.z + ) + else + return fast_new( + a.x * b, + a.y * b, + a.z * b + ) + end +end +function metatable.__mul(a, b) + if type(a) == "table" then + return fast_new( + a.x * b, + a.y * b, + a.z * b + ) + else + return fast_new( + a * b.x, + a * b.y, + a * b.z + ) + end +end + +function vector.divide(a, b) + if type(b) == "table" then + return fast_new( + a.x / b.x, + a.y / b.y, + a.z / b.z + ) + else + return fast_new( + a.x / b, + a.y / b, + a.z / b + ) + end +end +function metatable.__div(a, b) + -- scalar/vector makes no sense + return fast_new( + a.x / b, + a.y / b, + a.z / b + ) +end + +-- misc stuff + +function vector.offset(v, x, y, z) + return fast_new( + v.x + x, + v.y + y, + v.z + z + ) +end + +function vector.sort(a, b) + return fast_new(math.min(a.x, b.x), math.min(a.y, b.y), math.min(a.z, b.z)), + fast_new(math.max(a.x, b.x), math.max(a.y, b.y), math.max(a.z, b.z)) +end + +function vector.check(v) + return getmetatable(v) == metatable +end + +local function sin(x) + if x % math.pi == 0 then + return 0 + else + return math.sin(x) + end +end + +local function cos(x) + if x % math.pi == math.pi / 2 then + return 0 + else + return math.cos(x) + end +end + +function vector.rotate_around_axis(v, axis, angle) + local cosangle = cos(angle) + local sinangle = sin(angle) + axis = vector.normalize(axis) + -- https://en.wikipedia.org/wiki/Rodrigues%27_rotation_formula + local dot_axis = vector.multiply(axis, vector.dot(axis, v)) + local cross = vector.cross(v, axis) + return vector.new( + cross.x * sinangle + (v.x - dot_axis.x) * cosangle + dot_axis.x, + cross.y * sinangle + (v.y - dot_axis.y) * cosangle + dot_axis.y, + cross.z * sinangle + (v.z - dot_axis.z) * cosangle + dot_axis.z + ) +end + +function vector.rotate(v, rot) + local sinpitch = sin(-rot.x) + local sinyaw = sin(-rot.y) + local sinroll = sin(-rot.z) + local cospitch = cos(rot.x) + local cosyaw = cos(rot.y) + local cosroll = math.cos(rot.z) + -- Rotation matrix that applies yaw, pitch and roll + local matrix = { + { + sinyaw * sinpitch * sinroll + cosyaw * cosroll, + sinyaw * sinpitch * cosroll - cosyaw * sinroll, + sinyaw * cospitch, + }, + { + cospitch * sinroll, + cospitch * cosroll, + -sinpitch, + }, + { + cosyaw * sinpitch * sinroll - sinyaw * cosroll, + cosyaw * sinpitch * cosroll + sinyaw * sinroll, + cosyaw * cospitch, + }, + } + -- Compute matrix multiplication: `matrix` * `v` + return vector.new( + matrix[1][1] * v.x + matrix[1][2] * v.y + matrix[1][3] * v.z, + matrix[2][1] * v.x + matrix[2][2] * v.y + matrix[2][3] * v.z, + matrix[3][1] * v.x + matrix[3][2] * v.y + matrix[3][3] * v.z + ) +end + +function vector.dir_to_rotation(forward, up) + forward = vector.normalize(forward) + local rot = vector.new(math.asin(forward.y), -math.atan2(forward.x, forward.z), 0) + if not up then + return rot + end + assert(vector.dot(forward, up) < 0.000001, + "Invalid vectors passed to vector.dir_to_rotation().") + up = vector.normalize(up) + -- Calculate vector pointing up with roll = 0, just based on forward vector. + local forwup = vector.rotate(vector.new(0, 1, 0), rot) + -- 'forwup' and 'up' are now in a plane with 'forward' as normal. + -- The angle between them is the absolute of the roll value we're looking for. + rot.z = vector.angle(forwup, up) + + -- Since vector.angle never returns a negative value or a value greater + -- than math.pi, rot.z has to be inverted sometimes. + -- To determine wether this is the case, we rotate the up vector back around + -- the forward vector and check if it worked out. + local back = vector.rotate_around_axis(up, forward, -rot.z) + + -- We don't use vector.equals for this because of floating point imprecision. + if (back.x - forwup.x) * (back.x - forwup.x) + + (back.y - forwup.y) * (back.y - forwup.y) + + (back.z - forwup.z) * (back.z - forwup.z) > 0.0000001 then + rot.z = -rot.z + end + return rot +end diff --git a/mods/CORE/mcl_init/compatibility.lua b/mods/CORE/mcl_init/compatibility.lua index aed291f16..3f1347181 100644 --- a/mods/CORE/mcl_init/compatibility.lua +++ b/mods/CORE/mcl_init/compatibility.lua @@ -1,75 +1 @@ -mcl_compatibility = {} - -function vector.offset(v,x,y,z) - return vector.add(v,{x=x,y=y,z=z}) -end - -local minetest_get_node = minetest.get_node - -mcl_compatibility.sort_nodes = function(nodes) - if not nodes then return {} end - for _, pos in pairs(nodes) do - if not pos.x or not pos.y or not pos.z then - return nodes - end - end - local new_nodes = {} - for _, pos in pairs(nodes) do - local node = minetest_get_node(pos) - local name = node.name - if not new_nodes[name] then - new_nodes[name] = { pos } - else - table.insert(new_nodes[name], pos) - end - end - return new_nodes -end - -function mcl_vars.pos_to_block(pos) - return mcl_mapgen and mcl_mapgen.pos_to_block(pos) -end - -function mcl_vars.pos_to_chunk(pos) - return mcl_mapgen and mcl_mapgen.pos_to_chunk(pos) -end - -function mcl_vars.get_chunk_number(pos) - return mcl_mapgen and get_chunk_number(pos) -end - -function mcl_vars.is_generated(pos) - local node = minetest.get_node(pos) - if not node then return false end - if node.name == "ignore" then return false end - return true -end - -function mcl_vars.get_node(p, force, us_timeout) - if not p or not p.x or not p.y or not p.z then return {name="error"} end - local node = minetest.get_node(p) - if node.name ~= "ignore" then return node end - minetest.get_voxel_manip():read_from_map(p, p) - return minetest.get_node(pos) -end - -mcl_vars.mg_overworld_min = -62 -mcl_vars.mg_overworld_max_official = 198 -mcl_vars.mg_bedrock_overworld_min = -62 -mcl_vars.mg_bedrock_overworld_max = -58 -mcl_vars.mg_lava_overworld_max = -52 -mcl_vars.mg_lava = true -mcl_vars.mg_bedrock_is_rough = true -mcl_vars.mg_overworld_max = 30927 -mcl_vars.mg_nether_min = -29067 -mcl_vars.mg_nether_max = -28939 -mcl_vars.mg_bedrock_nether_bottom_min = -29067 -mcl_vars.mg_bedrock_nether_top_max = -29063 -mcl_vars.mg_end_min = -27073 -mcl_vars.mg_end_max_official = -26817 -mcl_vars.mg_end_max = -2062 -mcl_vars.mg_end_platform_pos = { x = 100, y = -26999, z = 0 } -mcl_vars.mg_realm_barrier_overworld_end_max = -2062 -mcl_vars.mg_realm_barrier_overworld_end_min = -2073 -mcl_vars.mg_dungeons = true - +-- moved to mcl_compatibility diff --git a/mods/CORE/mcl_init/init.lua b/mods/CORE/mcl_init/init.lua index 4e5b5b675..17945b472 100644 --- a/mods/CORE/mcl_init/init.lua +++ b/mods/CORE/mcl_init/init.lua @@ -1,5 +1,5 @@ -- Some global variables (don't overwrite them!) -mcl_vars = {} +mcl_vars = mcl_vars or {} mcl_vars.redstone_tick = 0.1 @@ -30,7 +30,3 @@ minetest.craftitemdef_default.stack_max = 64 -- Set random seed for all other mods (Remember to make sure no other mod calls this function) math.randomseed(os.time()) - -local modname = minetest.get_current_modname() -local modpath = minetest.get_modpath(modname) -dofile(modpath .. "/compatibility.lua") diff --git a/mods/CORE/mcl_init/mod.conf b/mods/CORE/mcl_init/mod.conf index a0e810a2f..5bf2fad76 100644 --- a/mods/CORE/mcl_init/mod.conf +++ b/mods/CORE/mcl_init/mod.conf @@ -1,3 +1,4 @@ name = mcl_init +depends = mcl_compatibility author = Wuzzy description = Initialization mod of MineClone 2. Defines some common shared variables and sets up initial default settings which have to be set at the beginning.