Refactor compass code.

* Split up `get_compass_image()` into smaller functions.  This allows
  for better code sharing between old and new API and globalstep fn.
* Add `get_compass_itemname()` function.  It will be the new API of
  choice, `get_compass_image() will be deprecated soon.
* Remove function declaration out of globalstep function.
* Various other performance improvements.
* Add local aliases for global functions
* Lodestone compasses can only stack 1 item.
* Document functions and variables.
* Fix lodetone compass inaccurately reusing compass descriptions.
* Add usage descriptions to node definitions
* Refactor craftitem registration code.
* Update translation templates.
This commit is contained in:
kabou 2022-05-10 18:28:57 +02:00
parent 4d342b8365
commit a8c231da34
8 changed files with 247 additions and 146 deletions

@ -1,16 +1,55 @@
local stereotype_frame = 18
local S = minetest.get_translator(minetest.get_current_modname()) local S = minetest.get_translator(minetest.get_current_modname())
mcl_compass = {} mcl_compass = {}
local compass_types = {
{
name = "compass",
desc = S("Compass"),
tt = S("Points to the world origin"),
longdesc = S("Compasses are tools which point to the world origin (X=0, Z=0) or the spawn point in the Overworld."),
usagehelp = S("A Compass always points to the world spawn point when the player is in the overworld. In other dimensions, it spins randomly."),
},
{
name = "compass_lodestone",
desc = S("Lodestone Compass"),
tt = S("Points to a lodestone"),
longdesc = S("Lodestone compasses resemble regular compasses, but they point to a specific lodestone."),
usagehelp = S("A Lodestone compass can be made from an ordinary compass by using it on a lodestone. After becoming a lodestone compass, it always points to its linked lodestone, provided that they are in the same dimension. If not in the same dimension, the lodestone compass spins randomly, similarly to a regular compass when outside the overworld. A lodestone compass can be relinked with another lodestone."),
}
}
-- Number of dynamic compass images (and items registered.)
local compass_frames = 32 local compass_frames = 32
-- The image/item that is craftable and shown in inventories.
local stereotype_frame = 18
-- random compass spinning tick in seconds.
-- Increase if there are performance problems.
local spin_timer_tick = 0.5
-- Local aliases to globals for better lua performance
local m_deg = math.deg
local m_atan2 = math.atan2
local m_floor = math.floor
local m_rnd = math.random
local vec_new = vector.new
local vec_from_str = vector.from_string
local get_connected_players = minetest.get_connected_players
local get_item_group = minetest.get_item_group
local setting_get_pos = minetest.setting_get_pos
local compass_works = mcl_worlds.compass_works
local y_to_layer = mcl_worlds.y_to_layer
-- Initialize random compass frame for spinning compass. It is updated in
-- the compass globalstep function.
local random_frame = m_rnd(0, compass_frames-1)
local function get_far_node(pos, itemstack) --code from minetest dev wiki: https://dev.minetest.net/minetest.get_node, some edits have been made to add a cooldown for force loads local function get_far_node(pos, itemstack) --code from minetest dev wiki: https://dev.minetest.net/minetest.get_node, some edits have been made to add a cooldown for force loads
local node = minetest.get_node(pos) local node = minetest.get_node(pos)
if node.name == "ignore" then if node.name == "ignore" then
tstamp = tonumber(itemstack:get_meta():get_string("last_forceload")) local tstamp = tonumber(itemstack:get_meta():get_string("last_forceload"))
if tstamp == nil then --this is only relevant for new lodestone compasses, the ones that have never performes a forceload yet if tstamp == nil then --this is only relevant for new lodestone compasses, the ones that have never performes a forceload yet
itemstack:get_meta():set_string("last_forceload", tostring(os.time(os.date("!*t")))) itemstack:get_meta():set_string("last_forceload", tostring(os.time(os.date("!*t"))))
tstamp = tonumber(os.time(os.date("!*t"))) tstamp = tonumber(os.time(os.date("!*t")))
@ -26,154 +65,180 @@ local function get_far_node(pos, itemstack) --code from minetest dev wiki: https
return node return node
end end
--- Get compass needle angle.
-- Returns the angle that the compass needle should point at expressed in
-- 360 degrees divided by the number of possible compass image frames..
--
-- pos: position of the compass;
-- target: position that the needle points towards;
-- dir: rotational direction of the compass.
--
local function get_compass_angle(pos, target, dir)
local angle_north = m_deg(m_atan2(target.x - pos.x, target.z - pos.z))
if angle_north < 0 then angle_north = angle_north + 360 end
local angle_dir = -m_deg(dir)
local angle_relative = (angle_north - angle_dir + 180) % 360
return m_floor((angle_relative/11.25) + 0.5) % compass_frames
end
--Not sure spawn point should be dymanic (is it in mc?) --- Get compass image frame.
--local default_spawn_settings = minetest.settings:get("static_spawnpoint") -- Returns the compass image frame with the needle direction matching the
-- compass' current position.
-- Timer for random compass spinning --
local random_timer = 0 -- pos: position of the compass;
local random_timer_trigger = 0.5 -- random compass spinning tick in seconds. Increase if there are performance problems -- dir: rotational direction of the compass.
-- itemstack: the compass including its optional lodestone metadata.
local random_frame = math.random(0, compass_frames-1) --
local function get_compass_frame(pos, dir, itemstack)
function mcl_compass.get_compass_image(pos, dir, itemstack) local lpos_str = itemstack:get_meta():get_string("pointsto")
if not itemstack then if lpos_str == "" then -- normal compass
minetest.log("WARNING: mcl_compass.get_compass_image() was called without itemstack, returning random frame!") -- Compasses only work in the overworld
if compass_works(pos) then
local spawn_pos = setting_get_pos("static_spawnpoint")
or vec_new(0, 0, 0)
return get_compass_angle(pos, spawn_pos, dir)
else
return random_frame return random_frame
end end
else -- lodestone compass
local lodestone_pos = minetest.string_to_pos(itemstack:get_meta():get_string("pointsto")) local lpos = vec_from_str(lpos_str)
local _, l_dim = y_to_layer(lpos.y)
if lodestone_pos then --lodestone meta present local _, p_dim = y_to_layer(pos.y)
local _, dim = mcl_worlds.y_to_layer(lodestone_pos.y) -- compass and lodestone must be in the same dimension
local _, playerdim = mcl_worlds.y_to_layer(pos.y) if l_dim == p_dim then
--check if lodestone still exists
if dim == playerdim then --Check if player and compass target are in the same dimension, above check is just if the diemension is valid for the non lodestone compass if get_far_node(lpos, itemstack).name == "mcl_compass:lodestone" then
return get_compass_angle(pos, lpos, dir)
if get_far_node(lodestone_pos, itemstack).name == "mcl_compass:lodestone" then --check if lodestone still exists
local angle_north = math.deg(math.atan2(lodestone_pos.x - pos.x, lodestone_pos.z - pos.z))
if angle_north < 0 then angle_north = angle_north + 360 end
local angle_dir = -math.deg(dir)
local angle_relative = (angle_north - angle_dir + 180) % 360
return math.floor((angle_relative/11.25) + 0.5) % compass_frames .. "_lodestone"
else -- lodestone got destroyed else -- lodestone got destroyed
return random_frame .. "_lodestone" return random_frame
end end
else
return random_frame .. "_lodestone"
end
else --no lodestone meta, normal compass....
local spawn = {x = 0, y=0, z=0} --before you guys tell me that the normal compass no points to real spawn, it always pointed to 0 0
local ssp = minetest.setting_get_pos("static_spawnpoint")
if ssp then
spawn = ssp
if type(spawn) ~= "table" or type(spawn.x) ~= "number" or type(spawn.y) ~= "number" or type(spawn.z) ~= "number" then
spawn = {x=0,y=0,z=0}
end
end
if mcl_worlds.compass_works(pos) then --is the player in the overworld?
local angle_north = math.deg(math.atan2(spawn.x - pos.x, spawn.z - pos.z))
if angle_north < 0 then angle_north = angle_north + 360 end
local angle_dir = -math.deg(dir)
local angle_relative = (angle_north - angle_dir + 180) % 360
return math.floor((angle_relative/11.25) + 0.5) % compass_frames
else else
return random_frame return random_frame
end end
end end
end end
--- Get partial compass itemname.
-- Returns partial itemname of a compass with needle direction matching compass position.
-- Legacy compatibility function for mods using older api.
--
function mcl_compass.get_compass_image(pos, dir, itemstack)
minetest.log("warning", "mcl_compass: deprecated function " ..
"get_compass_image() called, use get_compass_itemname().")
local itemstack = ItemStack("mcl_compass:" .. stereotype_frame)
local frame = get_compass_frame(pos, dir, itemstack)
if itemstack:get_meta():get_string("pointsto") ~= "" then
return frame .. "_lodestone"
else
return frame
end
end
--- Get compass itemname.
-- Returns the itemname of a compass with needle direction matching the
-- current compass position.
--
-- pos: position of the compass;
-- dir: rotational orientation of the compass;
-- itemstack: the compass including its optional lodestone metadata.
--
function mcl_compass.get_compass_itemname(pos, dir, itemstack)
if not itemstack then
minetest.log("warning", "mcl_compass.get_compass_image called without itemstack!")
return "mcl_compass:" .. stereotype_frame
end
local frame = get_compass_frame(pos, dir, itemstack)
if itemstack:get_meta():get_string("pointsto") ~= "" then
return "mcl_compass:" .. frame .. "_lodestone"
else
return "mcl_compass:" .. frame
end
end
-- Timer for randomly spinning compass.
-- Gets updated and checked in the globalstep function.
local spin_timer = 0
-- Compass globalstep function.
-- * updates random spin counter and random frame of spinning compasses;
-- * updates all compasses in player's inventories to match the correct
-- needle orientations for their current positions.
--
minetest.register_globalstep(function(dtime) minetest.register_globalstep(function(dtime)
random_timer = random_timer + dtime spin_timer = spin_timer + dtime
if spin_timer >= spin_timer_tick then
if random_timer >= random_timer_trigger then random_frame = (random_frame + m_rnd(-1, 1)) % compass_frames
random_frame = (random_frame + math.random(-1, 1)) % compass_frames spin_timer = 0
random_timer = 0
end
for _,player in pairs(minetest.get_connected_players()) do
local function has_compass(player)
for _,stack in pairs(player:get_inventory():get_list("main")) do
if minetest.get_item_group(stack:get_name(), "compass") ~= 0 then
return true
end
end
return false
end
if has_compass(player) then
local pos = player:get_pos()
for j,stack in pairs(player:get_inventory():get_list("main")) do
if minetest.get_item_group(stack:get_name(), "compass") ~= 0 then
local compass_image = mcl_compass.get_compass_image(pos, player:get_look_horizontal(), stack)
if minetest.get_item_group(stack:get_name(), "compass")-1 ~= compass_image and minetest.get_item_group(stack:get_name(), "compass")-1 .. "_lodestone" ~=compass_image then --Explaination: First check for normal compasses, secound check for lodestone ones
local itemname = "mcl_compass:"..compass_image
--minetest.log(os.time(os.date("!*t")))
stack:set_name(itemname)
player:get_inventory():set_stack("main", j, stack)
end
end end
local compass_nr, compass_frame
local pos, dir, inv
for _, player in pairs(get_connected_players()) do
pos = player:get_pos()
dir = player:get_look_horizontal()
inv = player:get_inventory()
for j, stack in pairs(inv:get_list("main")) do
compass_nr = get_item_group(stack:get_name(), "compass")
if compass_nr ~= 0 then
-- check if current compass image still matches true orientation
compass_frame = get_compass_frame(pos, dir, stack)
if compass_nr - 1 ~= compass_frame then
if stack:get_meta():get_string("pointsto") == "" then
stack:set_name("mcl_compass:" .. compass_frame)
else
stack:set_name("mcl_compass:" .. compass_frame .. "_lodestone")
end
inv:set_stack("main", j, stack)
end
end end
end end
end end
end) end)
local images = {} --
for frame = 0, compass_frames-1 do -- Node and craftitem definitions
local s = string.format("%02d", frame) --
table.insert(images, "mcl_compass_compass_"..s..".png")
end
local doc_mod = minetest.get_modpath("doc") local doc_mod = minetest.get_modpath("doc")
for i,img in ipairs(images) do for _, item in pairs(compass_types) do
local inv = 1 local name_fmt, img_fmt, stack_max
if item.name == "compass" then
name_fmt = "mcl_compass:%d"
img_fmt = "mcl_compass_compass_%02d.png"
stack_max = 64
elseif item.name == "compass_lodestone" then
name_fmt = "mcl_compass:%d_lodestone"
img_fmt = "mcl_compass_compass_%02d.png^[colorize:purple:50"
stack_max = 1
end
for i = 0, compass_frames - 1 do
local def = {
description = item.desc,
_tt_help = item.tt,
inventory_image = string.format(img_fmt, i),
wield_image = string.format(img_fmt, i),
stack_max = stack_max,
groups = {compass = i + 1, tool = 1, disable_repair = 1},
}
if i == stereotype_frame then if i == stereotype_frame then
inv = 0 def._doc_items_longdesc = item.longdesc
def._doc_items_usagehelp = item.usagehelp
else
def._doc_items_create_entry = false
def.groups.not_in_creative_inventory = 1
end end
local use_doc, longdesc, tt local itemstring = string.format(name_fmt, i)
--Why is there no usage help? This should be fixed. minetest.register_craftitem(itemstring, table.copy(def))
--local usagehelp
use_doc = i == stereotype_frame
if use_doc then
tt = S("Points to the world origin")
longdesc = S("Compasses are tools which point to the world origin (X=0, Z=0) or the spawn point in the Overworld.")
end
local itemstring = "mcl_compass:"..(i-1)
minetest.register_craftitem(itemstring, {
description = S("Compass"),
_tt_help = tt,
_doc_items_create_entry = use_doc,
_doc_items_longdesc = longdesc,
--_doc_items_usagehelp = usagehelp,
inventory_image = img,
wield_image = img,
stack_max = 64,
groups = {not_in_creative_inventory=inv, compass=i, tool=1, disable_repair=1 }
})
minetest.register_craftitem(itemstring .. "_lodestone", {
description = S("Lodestone Compass"),
_tt_help = tt,
_doc_items_create_entry = use_doc,
_doc_items_longdesc = longdesc,
--_doc_items_usagehelp = usagehelp,
inventory_image = img .. "^[colorize:purple:50",
wield_image = img .. "^[colorize:purple:50",
stack_max = 64,
groups = {not_in_creative_inventory=1, compass=i, tool=1, disable_repair=1 }
})
-- Help aliases. Makes sure the lookup tool works correctly -- Help aliases. Makes sure the lookup tool works correctly
if not use_doc and doc_mod then if doc_mod and i ~= stereotype_frame then
doc.add_entry_alias("craftitems", "mcl_compass:"..(stereotype_frame-1), "craftitems", itemstring) doc.add_entry_alias("craftitems", "mcl_compass:"..(stereotype_frame), "craftitems", itemstring)
end
end end
end end
minetest.register_craft({ minetest.register_craft({
output = "mcl_compass:"..stereotype_frame, output = "mcl_compass:" .. stereotype_frame,
recipe = { recipe = {
{"", "mcl_core:iron_ingot", ""}, {"", "mcl_core:iron_ingot", ""},
{"mcl_core:iron_ingot", "mesecons:redstone", "mcl_core:iron_ingot"}, {"mcl_core:iron_ingot", "mesecons:redstone", "mcl_core:iron_ingot"},
@ -181,19 +246,10 @@ minetest.register_craft({
} }
}) })
minetest.register_craft({ minetest.register_alias("mcl_compass:compass", "mcl_compass:" .. stereotype_frame)
output = "mcl_compass:lodestone",
recipe = {
{"mcl_core:stonebrickcarved","mcl_core:stonebrickcarved","mcl_core:stonebrickcarved"},
{"mcl_core:stonebrickcarved", "mcl_core:diamondblock", "mcl_core:stonebrickcarved"},
{"mcl_core:stonebrickcarved", "mcl_core:stonebrickcarved", "mcl_core:stonebrickcarved"}
}
})
minetest.register_alias("mcl_compass:compass", "mcl_compass:"..stereotype_frame)
-- Export stereotype item for other mods to use -- Export stereotype item for other mods to use
mcl_compass.stereotype = "mcl_compass:"..tostring(stereotype_frame) mcl_compass.stereotype = "mcl_compass:" .. stereotype_frame
minetest.register_node("mcl_compass:lodestone",{ minetest.register_node("mcl_compass:lodestone",{
@ -218,3 +274,12 @@ minetest.register_node("mcl_compass:lodestone",{
_mcl_blast_resistance = 6, _mcl_blast_resistance = 6,
sounds = mcl_sounds.node_sound_stone_defaults() sounds = mcl_sounds.node_sound_stone_defaults()
}) })
minetest.register_craft({
output = "mcl_compass:lodestone",
recipe = {
{"mcl_core:stonebrickcarved","mcl_core:stonebrickcarved","mcl_core:stonebrickcarved"},
{"mcl_core:stonebrickcarved", "mcl_core:diamondblock", "mcl_core:stonebrickcarved"},
{"mcl_core:stonebrickcarved", "mcl_core:stonebrickcarved", "mcl_core:stonebrickcarved"}
}
})

@ -1,4 +1,9 @@
# textdomain: mcl_compass # textdomain: mcl_compass
Compasses are tools which point to the world origin (X@=0, Z@=0) or the spawn point in the Overworld.=Kompasse sind Werkzeuge, die zum Ursprungspunkt der Welt (X@=0, Z@=0) oder zum Einstiegspunkt der Welt zeigen.
Compass=Kompass Compass=Kompass
Points to the world origin=Zeigt zum Startpunkt der Welt Points to the world origin=Zeigt zum Startpunkt der Welt
Compasses are tools which point to the world origin (X@=0, Z@=0) or the spawn point in the Overworld.=Kompasse sind Werkzeuge, die zum Ursprungspunkt der Welt (X@=0, Z@=0) oder zum Einstiegspunkt der Welt zeigen.
A Compass always points to the world spawn point when the player is in the overworld. In other dimensions, it spins randomly.=
Lodestone Compass=
Points to a lodestone=
Lodestone compasses resemble regular compasses, but they point to a specific lodestone.=
A Lodestone compass can be made from an ordinary compass by using it on a lodestone. After becoming a lodestone compass, it always points to its linked lodestone, provided that they are in the same dimension. If not in the same dimension, the lodestone compass spins randomly, similarly to a regular compass when outside the overworld. A lodestone compass can be relinked with another lodestone.=

@ -1,3 +1,9 @@
# textdomain: mcl_compass # textdomain: mcl_compass
Compasses are tools which point to the world origin (X@=0, Z@=0) or the spawn point in the Overworld.=Las brújulas son herramientas que apuntan al origen del mundo (X @ = 0, Z @ = 0) o al punto de generación en el mundo.
Compass=Brújula Compass=Brújula
Points to the world origin=
Compasses are tools which point to the world origin (X@=0, Z@=0) or the spawn point in the Overworld.=Las brújulas son herramientas que apuntan al origen del mundo (X @ = 0, Z @ = 0) o al punto de generación en el mundo.
A Compass always points to the world spawn point when the player is in the overworld. In other dimensions, it spins randomly.=
Lodestone Compass=
Points to a lodestone=
Lodestone compasses resemble regular compasses, but they point to a specific lodestone.=
A Lodestone compass can be made from an ordinary compass by using it on a lodestone. After becoming a lodestone compass, it always points to its linked lodestone, provided that they are in the same dimension. If not in the same dimension, the lodestone compass spins randomly, similarly to a regular compass when outside the overworld. A lodestone compass can be relinked with another lodestone.=

@ -1,4 +1,9 @@
# textdomain: mcl_compass # textdomain: mcl_compass
Compasses are tools which point to the world origin (X@=0, Z@=0) or the spawn point in the Overworld.=Les boussoles sont des outils qui pointent vers l'origine du monde (X@=0,Z@=0) ou le point d'apparition dans l'Overworld.
Compass=Boussole Compass=Boussole
Points to the world origin=Pointe vers l'origine mondiale Points to the world origin=Pointe vers l'origine mondiale
Compasses are tools which point to the world origin (X@=0, Z@=0) or the spawn point in the Overworld.=Les boussoles sont des outils qui pointent vers l'origine du monde (X@=0,Z@=0) ou le point d'apparition dans l'Overworld.
A Compass always points to the world spawn point when the player is in the overworld. In other dimensions, it spins randomly.=
Lodestone Compass=
Points to a lodestone=
Lodestone compasses resemble regular compasses, but they point to a specific lodestone.=
A Lodestone compass can be made from an ordinary compass by using it on a lodestone. After becoming a lodestone compass, it always points to its linked lodestone, provided that they are in the same dimension. If not in the same dimension, the lodestone compass spins randomly, similarly to a regular compass when outside the overworld. A lodestone compass can be relinked with another lodestone.=

@ -1,4 +1,9 @@
# textdomain: mcl_compass # textdomain: mcl_compass
Compasses are tools which point to the world origin (X@=0, Z@=0) or the spawn point in the Overworld.=Kompasy to narzędzia które wskazują na punkt początku świata (X@=0, Z@=0) lub na miejsce odrodzenia na Powierzchni.
Compass=Kompas Compass=Kompas
Points to the world origin=Wskazuje na początek świata Points to the world origin=Wskazuje na początek świata
Compasses are tools which point to the world origin (X@=0, Z@=0) or the spawn point in the Overworld.=Kompasy to narzędzia które wskazują na punkt początku świata (X@=0, Z@=0) lub na miejsce odrodzenia na Powierzchni.
A Compass always points to the world spawn point when the player is in the overworld. In other dimensions, it spins randomly.=
Lodestone Compass=
Points to a lodestone=
Lodestone compasses resemble regular compasses, but they point to a specific lodestone.=
A Lodestone compass can be made from an ordinary compass by using it on a lodestone. After becoming a lodestone compass, it always points to its linked lodestone, provided that they are in the same dimension. If not in the same dimension, the lodestone compass spins randomly, similarly to a regular compass when outside the overworld. A lodestone compass can be relinked with another lodestone.=

@ -1,4 +1,9 @@
# textdomain: mcl_compass # textdomain: mcl_compass
Compasses are tools which point to the world origin (X@=0, Z@=0) or the spawn point in the Overworld.=Компас - инструмент, показывающий на начало мира (X@=0, Z@=0) или на точку возрождения в Верхнем Мире.
Compass=Компас Compass=Компас
Points to the world origin=Указывает на начало мира Points to the world origin=Указывает на начало мира
Compasses are tools which point to the world origin (X@=0, Z@=0) or the spawn point in the Overworld.=Компас - инструмент, показывающий на начало мира (X@=0, Z@=0) или на точку возрождения в Верхнем Мире.
A Compass always points to the world spawn point when the player is in the overworld. In other dimensions, it spins randomly.=
Lodestone Compass=
Points to a lodestone=
Lodestone compasses resemble regular compasses, but they point to a specific lodestone.=
A Lodestone compass can be made from an ordinary compass by using it on a lodestone. After becoming a lodestone compass, it always points to its linked lodestone, provided that they are in the same dimension. If not in the same dimension, the lodestone compass spins randomly, similarly to a regular compass when outside the overworld. A lodestone compass can be relinked with another lodestone.=

@ -1,4 +1,9 @@
# textdomain: mcl_compass # textdomain: mcl_compass
Compasses are tools which point to the world origin (X@=0, Z@=0) or the spawn point in the Overworld.=指南針是指向世界原點X@=0Z@=0或主世界的出生點的工具。
Compass=指南針 Compass=指南針
Points to the world origin=指向世界原點 Points to the world origin=指向世界原點
Compasses are tools which point to the world origin (X@=0, Z@=0) or the spawn point in the Overworld.=指南針是指向世界原點X@=0Z@=0或主世界的出生點的工具。
A Compass always points to the world spawn point when the player is in the overworld. In other dimensions, it spins randomly.=
Lodestone Compass=
Points to a lodestone=
Lodestone compasses resemble regular compasses, but they point to a specific lodestone.=
A Lodestone compass can be made from an ordinary compass by using it on a lodestone. After becoming a lodestone compass, it always points to its linked lodestone, provided that they are in the same dimension. If not in the same dimension, the lodestone compass spins randomly, similarly to a regular compass when outside the overworld. A lodestone compass can be relinked with another lodestone.=

@ -1,4 +1,9 @@
# textdomain: mcl_compass # textdomain: mcl_compass
Compasses are tools which point to the world origin (X@=0, Z@=0) or the spawn point in the Overworld.=
Compass= Compass=
Points to the world origin= Points to the world origin=
Compasses are tools which point to the world origin (X@=0, Z@=0) or the spawn point in the Overworld.=
A Compass always points to the world spawn point when the player is in the overworld. In other dimensions, it spins randomly.=
Lodestone Compass=
Points to a lodestone=
Lodestone compasses resemble regular compasses, but they point to a specific lodestone.=
A Lodestone compass can be made from an ordinary compass by using it on a lodestone. After becoming a lodestone compass, it always points to its linked lodestone, provided that they are in the same dimension. If not in the same dimension, the lodestone compass spins randomly, similarly to a regular compass when outside the overworld. A lodestone compass can be relinked with another lodestone.=