2021-05-29 17:12:33 +03:00
local S = minetest.get_translator ( minetest.get_current_modname ( ) )
2019-03-08 00:40:43 +02:00
2017-02-20 22:14:10 +02:00
mcl_compass = { }
2015-06-29 20:55:56 +03:00
2022-05-10 19:28:57 +03:00
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. " ) ,
2022-12-01 22:13:41 +02:00
} ,
{
name = " compass_recovery " ,
desc = S ( " Recovery Compass " ) ,
2022-12-01 22:20:39 +02:00
tt = S ( " Points to your last death location " ) ,
2022-12-01 22:13:41 +02:00
longdesc = S ( " Recovery Compasses are compasses that point to your last death location " ) ,
usagehelp = S ( " Recovery Compasses always point to the location of your last death, in case you haven't died yet, it will just randomly spin around " ) ,
2022-05-10 19:28:57 +03:00
}
}
-- Number of dynamic compass images (and items registered.)
2017-08-17 22:26:09 +03:00
local compass_frames = 32
2022-05-10 19:28:57 +03:00
-- 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
2022-05-11 22:31:50 +03:00
local string_find = string.find
2022-05-11 18:41:10 +03:00
local string_to_pos = minetest.string_to_pos
2022-05-10 19:28:57 +03:00
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 )
2022-04-30 11:57:03 +03:00
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 )
if node.name == " ignore " then
2022-05-10 19:28:57 +03:00
local tstamp = tonumber ( itemstack : get_meta ( ) : get_string ( " last_forceload " ) )
2022-04-30 11:57:03 +03:00
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 " ) ) ) )
tstamp = tonumber ( os.time ( os.date ( " !*t " ) ) )
end
if tonumber ( os.time ( os.date ( " !*t " ) ) ) - tstamp > 180 then --current time in secounds - old time in secounds, if it is over 180 (3 mins): forceload
itemstack : get_meta ( ) : set_string ( " last_forceload " , tostring ( os.time ( os.date ( " !*t " ) ) ) )
minetest.get_voxel_manip ( ) : read_from_map ( pos , pos )
node = minetest.get_node ( pos )
else
node = { name = " mcl_compass:lodestone " } --cooldown not over yet, pretend like there is something...
end
end
return node
end
2022-05-10 19:28:57 +03:00
--- 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
2022-04-30 11:57:03 +03:00
2022-05-10 19:28:57 +03:00
--- Get compass image frame.
-- Returns the compass image frame with the needle direction matching the
-- compass' current position.
--
-- pos: position of the compass;
-- dir: rotational direction of the compass.
-- itemstack: the compass including its optional lodestone metadata.
--
local function get_compass_frame ( pos , dir , itemstack )
2022-05-11 22:31:50 +03:00
if not string_find ( itemstack : get_name ( ) , " _lodestone " ) then -- normal compass
2022-05-10 19:28:57 +03:00
-- 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 )
2022-04-30 11:57:03 +03:00
else
2022-05-10 19:28:57 +03:00
return random_frame
2022-04-30 11:57:03 +03:00
end
2022-05-10 19:28:57 +03:00
else -- lodestone compass
2022-05-11 22:31:50 +03:00
local lpos_str = itemstack : get_meta ( ) : get_string ( " pointsto " )
2022-05-11 18:41:10 +03:00
local lpos = string_to_pos ( lpos_str )
if not lpos then
minetest.log ( " warning " , " mcl_compass: invalid lodestone position! " )
return random_frame
end
2022-05-10 19:28:57 +03:00
local _ , l_dim = y_to_layer ( lpos.y )
local _ , p_dim = y_to_layer ( pos.y )
-- compass and lodestone must be in the same dimension
if l_dim == p_dim then
--check if lodestone still exists
if get_far_node ( lpos , itemstack ) . name == " mcl_compass:lodestone " then
return get_compass_angle ( pos , lpos , dir )
else -- lodestone got destroyed
return random_frame
2021-05-03 12:22:47 +03:00
end
2022-04-30 11:57:03 +03:00
else
return random_frame
end
2022-05-10 19:28:57 +03:00
end
end
2022-04-30 11:57:03 +03:00
2022-05-11 00:38:28 +03:00
-- Export stereotype item for other mods to use
mcl_compass.stereotype = " mcl_compass: " .. stereotype_frame
2022-05-10 19:28:57 +03:00
--- Get partial compass itemname.
-- Returns partial itemname of a compass with needle direction matching compass position.
-- Legacy compatibility function for mods using older api.
--
2022-05-11 00:38:28 +03:00
function mcl_compass . get_compass_image ( pos , dir )
2022-05-10 19:28:57 +03:00
minetest.log ( " warning " , " mcl_compass: deprecated function " ..
" get_compass_image() called, use get_compass_itemname(). " )
2022-05-11 00:38:28 +03:00
local itemstack = ItemStack ( mcl_compass.stereotype )
2022-05-11 22:43:52 +03:00
return get_compass_frame ( pos , dir , itemstack )
2021-05-03 12:22:47 +03:00
end
2022-05-10 19:28:57 +03:00
--- 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
2017-08-17 22:26:09 +03:00
2022-05-10 19:28:57 +03:00
-- 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 )
spin_timer = spin_timer + dtime
if spin_timer >= spin_timer_tick then
random_frame = ( random_frame + m_rnd ( - 1 , 1 ) ) % compass_frames
spin_timer = 0
2017-08-17 22:26:09 +03:00
end
2022-05-10 19:28:57 +03:00
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 " )
2022-12-01 22:13:41 +02:00
if compass_nr ~= 0 and not string_find ( stack : get_name ( ) , " _recovery " ) then
2022-05-10 19:28:57 +03:00
-- check if current compass image still matches true orientation
compass_frame = get_compass_frame ( pos , dir , stack )
if compass_nr - 1 ~= compass_frame then
2022-05-11 22:31:50 +03:00
if string_find ( stack : get_name ( ) , " _lodestone " ) then
2022-05-10 19:28:57 +03:00
stack : set_name ( " mcl_compass: " .. compass_frame .. " _lodestone " )
2022-06-12 00:31:57 +03:00
awards.unlock ( player : get_player_name ( ) , " mcl:countryLode " )
2022-05-11 22:31:50 +03:00
else
stack : set_name ( " mcl_compass: " .. compass_frame )
2022-04-30 11:57:03 +03:00
end
2022-05-10 19:28:57 +03:00
inv : set_stack ( " main " , j , stack )
2015-06-29 20:55:56 +03:00
end
2022-12-02 16:43:08 +02:00
elseif compass_nr ~= 0 then
2022-12-01 22:13:41 +02:00
local meta = player : get_meta ( )
local posstring = meta : get_string ( " mcl_compass:recovery_pos " )
if not posstring or posstring == " " then
stack : set_name ( " mcl_compass: " .. random_frame .. " _recovery " )
else
local targetpos = minetest.string_to_pos ( posstring )
2022-12-02 19:52:31 +02:00
local _ , target_dim = y_to_layer ( targetpos.y )
local _ , p_dim = y_to_layer ( pos.y )
if p_dim ~= target_dim then
stack : set_name ( " mcl_compass: " .. random_frame .. " _recovery " )
else
stack : set_name ( " mcl_compass: " .. get_compass_angle ( pos , targetpos , dir ) .. " _recovery " )
end
2022-12-01 22:13:41 +02:00
end
inv : set_stack ( " main " , j , stack )
2015-06-29 20:55:56 +03:00
end
end
end
end )
2022-05-10 19:28:57 +03:00
--
-- Node and craftitem definitions
--
2021-05-29 17:12:33 +03:00
local doc_mod = minetest.get_modpath ( " doc " )
2017-03-20 19:12:05 +02:00
2022-05-10 19:28:57 +03:00
for _ , item in pairs ( compass_types ) do
2022-05-11 22:45:15 +03:00
local name_fmt , img_fmt
2022-05-10 19:28:57 +03:00
if item.name == " compass " then
name_fmt = " mcl_compass:%d "
img_fmt = " mcl_compass_compass_%02d.png "
elseif item.name == " compass_lodestone " then
name_fmt = " mcl_compass:%d_lodestone "
img_fmt = " mcl_compass_compass_%02d.png^[colorize:purple:50 "
2022-12-01 22:13:41 +02:00
elseif item.name == " compass_recovery " then
name_fmt = " mcl_compass:%d_recovery "
img_fmt = " mcl_compass_recovery_compass_%02d.png "
2017-03-02 22:55:25 +02:00
end
2022-05-10 19:28:57 +03:00
for i = 0 , compass_frames - 1 do
2022-05-12 19:34:48 +03:00
local itemstring = string.format ( name_fmt , i )
2022-05-10 19:28:57 +03:00
local def = {
description = item.desc ,
_tt_help = item.tt ,
inventory_image = string.format ( img_fmt , i ) ,
wield_image = string.format ( img_fmt , i ) ,
groups = { compass = i + 1 , tool = 1 , disable_repair = 1 } ,
}
if i == stereotype_frame then
def._doc_items_longdesc = item.longdesc
def._doc_items_usagehelp = item.usagehelp
2022-05-12 19:34:48 +03:00
if string.match ( itemstring , " lodestone " ) then
def.groups . not_in_creative_inventory = 1
end
2022-05-10 19:28:57 +03:00
else
def._doc_items_create_entry = false
def.groups . not_in_creative_inventory = 1
end
minetest.register_craftitem ( itemstring , table.copy ( def ) )
-- Help aliases. Makes sure the lookup tool works correctly
if doc_mod and i ~= stereotype_frame then
doc.add_entry_alias ( " craftitems " , " mcl_compass: " .. ( stereotype_frame ) , " craftitems " , itemstring )
end
2017-03-20 19:12:05 +02:00
end
2015-06-29 20:55:56 +03:00
end
minetest.register_craft ( {
2022-05-10 19:28:57 +03:00
output = " mcl_compass: " .. stereotype_frame ,
2015-06-29 20:55:56 +03:00
recipe = {
2021-05-29 17:12:33 +03:00
{ " " , " mcl_core:iron_ingot " , " " } ,
{ " mcl_core:iron_ingot " , " mesecons:redstone " , " mcl_core:iron_ingot " } ,
{ " " , " mcl_core:iron_ingot " , " " }
2015-06-29 20:55:56 +03:00
}
2017-01-04 06:04:13 +02:00
} )
2017-01-09 04:26:00 +02:00
2022-12-02 16:40:18 +02:00
minetest.register_craft ( { --TODO: update once echo shards are a thing
output = " mcl_compass: " .. random_frame .. " _recovery " ,
recipe = {
{ " " , " mcl_nether:netherite_ingot " , " " } ,
{ " mcl_core:diamondblock " , " mcl_compass: " .. stereotype_frame , " mcl_core:diamondblock " } ,
{ " mcl_core:diamondblock " , " mcl_core:diamondblock " , " mcl_core:diamondblock " }
}
} )
2022-05-10 19:28:57 +03:00
minetest.register_alias ( " mcl_compass:compass " , " mcl_compass: " .. stereotype_frame )
2017-02-20 22:14:10 +02:00
2022-04-30 11:57:03 +03:00
minetest.register_node ( " mcl_compass:lodestone " , {
description = S ( " Lodestone " ) ,
on_rightclick = function ( pos , node , player , itemstack )
2022-05-11 22:31:50 +03:00
local name = itemstack.get_name ( itemstack )
if string_find ( name , " mcl_compass: " ) then
if name ~= " mcl_compass:lodestone " then
2022-04-30 11:57:03 +03:00
itemstack : get_meta ( ) : set_string ( " pointsto " , minetest.pos_to_string ( pos ) )
2022-05-11 22:31:50 +03:00
local dir = player : get_look_horizontal ( )
local frame = get_compass_frame ( pos , dir , itemstack )
itemstack : set_name ( " mcl_compass: " .. frame .. " _lodestone " )
2022-04-30 11:57:03 +03:00
end
end
end ,
tiles = {
" lodestone_top.png " ,
" lodestone_bottom.png " ,
" lodestone_side1.png " ,
" lodestone_side2.png " ,
" lodestone_side3.png " ,
" lodestone_side4.png "
} ,
groups = { pickaxey = 1 , material_stone = 1 } ,
_mcl_hardness = 1.5 ,
_mcl_blast_resistance = 6 ,
sounds = mcl_sounds.node_sound_stone_defaults ( )
} )
2022-05-10 19:28:57 +03:00
minetest.register_craft ( {
output = " mcl_compass:lodestone " ,
recipe = {
{ " mcl_core:stonebrickcarved " , " mcl_core:stonebrickcarved " , " mcl_core:stonebrickcarved " } ,
2022-06-27 00:26:44 +03:00
{ " mcl_core:stonebrickcarved " , " mcl_nether:netherite_ingot " , " mcl_core:stonebrickcarved " } ,
2022-05-10 19:28:57 +03:00
{ " mcl_core:stonebrickcarved " , " mcl_core:stonebrickcarved " , " mcl_core:stonebrickcarved " }
}
} )
2022-12-01 22:13:41 +02:00
--set recovery meta
minetest.register_on_dieplayer ( function ( player )
local meta = player : get_meta ( ) ;
meta : set_string ( " mcl_compass:recovery_pos " , minetest.pos_to_string ( player : get_pos ( ) ) )
end )