Repixture/mods/rp_nav/compass.lua

526 lines
17 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

--
-- Compass handling
--
local S = minetest.get_translator("rp_nav")
local wield_image_0 = "nav_compass_inventory_0.png"
local wield_image_1 = "nav_compass_inventory_1.png"
local magno_wield_image_0 = "rp_nav_magnocompass_inventory_0.png"
local magno_wield_image_1 = "rp_nav_magnocompass_inventory_1.png"
local yaw_to_compass_dir = function(yaw)
local dir = math.floor(((yaw / math.pi) * 4) + 0.5)
if dir < 0 then
dir = dir + 8
end
if dir >= 8 then
dir = 0
end
return dir
end
local update_compass_itemstack = function(itemstack, pos, lookyaw)
local item = minetest.registered_items[itemstack:get_name()]
local new_itemstack = ItemStack(itemstack)
local changed = false
local lookdir = yaw_to_compass_dir(lookyaw)
if item ~= nil then
-- normal compass
if item.groups.nav_compass == 1 then
new_itemstack:set_name("rp_nav:compass_"..lookdir)
changed = true
-- magnocompass
elseif item.groups.nav_compass == 2 then
local meta = itemstack:get_meta()
local x, y, z = meta:get_int("magno_x"), meta:get_int("magno_y"), meta:get_int("magno_z")
if not x or not y or not z then
-- Fallback pos
x, y, z = 0, 0, 0
end
local magno_pos = vector.new(x, 0, z)
local check_pos = vector.new(pos.x, 0, pos.z)
local vdir = vector.direction(magno_pos, check_pos)
local magnoyaw = minetest.dir_to_yaw(vdir)
magnoyaw = (math.pi - magnoyaw + lookyaw) % (math.pi*2)
local dir = yaw_to_compass_dir(magnoyaw)
new_itemstack:set_name("rp_nav:magnocompass_"..dir)
changed = true
end
end
return new_itemstack, changed
end
local function on_globalstep(dtime)
for _, player in pairs(minetest.get_connected_players()) do
local inv = player:get_inventory()
local yaw = player:get_look_horizontal()
local pos = player:get_pos()
-- Cycle through all inventory slots
for i = 1, inv:get_size("main") do
local itemstack = inv:get_stack("main", i)
local changed
itemstack, changed = update_compass_itemstack(itemstack, pos, yaw)
if changed then
inv:set_stack("main", i, itemstack)
end
end
end
end
minetest.register_globalstep(on_globalstep)
-- Items
local inv_imgs = {
[0] = "nav_compass_inventory_0.png",
"nav_compass_inventory_1.png",
"nav_compass_inventory_0.png^[transformR270",
"nav_compass_inventory_1.png^[transformR270",
"nav_compass_inventory_0.png^[transformR180",
"nav_compass_inventory_1.png^[transformR180",
"nav_compass_inventory_0.png^[transformR90",
"nav_compass_inventory_1.png^[transformR90",
}
local wield_imgs = {
[0] = wield_image_0,
wield_image_1,
wield_image_0 .. "^[transformR270",
wield_image_1 .. "^[transformR270",
wield_image_0 .. "^[transformR180",
wield_image_1 .. "^[transformR180",
wield_image_0 .. "^[transformR90",
wield_image_1 .. "^[transformR90",
}
local node_imgs = {
[0] = "rp_nav_compass_0.png",
"rp_nav_compass_1.png",
"rp_nav_compass_0.png^[transformR270",
"rp_nav_compass_1.png^[transformR270",
"rp_nav_compass_0.png^[transformR180",
"rp_nav_compass_1.png^[transformR180",
"rp_nav_compass_0.png^[transformR90",
"rp_nav_compass_1.png^[transformR90",
}
local inv_imgs_magno = {
[0] = "rp_nav_magnocompass_inventory_0.png",
"rp_nav_magnocompass_inventory_1.png",
"rp_nav_magnocompass_inventory_0.png^[transformR270",
"rp_nav_magnocompass_inventory_1.png^[transformR270",
"rp_nav_magnocompass_inventory_0.png^[transformR180",
"rp_nav_magnocompass_inventory_1.png^[transformR180",
"rp_nav_magnocompass_inventory_0.png^[transformR90",
"rp_nav_magnocompass_inventory_1.png^[transformR90",
}
local node_imgs_magno = {
[0] = "rp_nav_magnocompass_0.png",
"rp_nav_magnocompass_1.png",
"rp_nav_magnocompass_0.png^[transformR270",
"rp_nav_magnocompass_1.png^[transformR270",
"rp_nav_magnocompass_0.png^[transformR180",
"rp_nav_magnocompass_1.png^[transformR180",
"rp_nav_magnocompass_0.png^[transformR90",
"rp_nav_magnocompass_1.png^[transformR90",
}
local wield_imgs_magno = {
[0] = magno_wield_image_0,
magno_wield_image_1,
magno_wield_image_0 .. "^[transformR270",
magno_wield_image_1 .. "^[transformR270",
magno_wield_image_0 .. "^[transformR180",
magno_wield_image_1 .. "^[transformR180",
magno_wield_image_0 .. "^[transformR90",
magno_wield_image_1 .. "^[transformR90",
}
local d = S("Compass")
local t = S("It points to the North") .. "\n" .. S("Can be magnetized at magnetic blocks")
local dm = S("Magno Compass")
local tm = S("It points to a position") .. "\n" .. S("Can be demagnetized at unmagnetic blocks")
-- Magnetize the compass item (`itemstack`) to point at position `magnet_pos`,
-- playing a confirm sound at `sound_pos`.
-- `achievement_player` is a player object if there is an associated player. It is used
-- to trigger the "True Navigator" achievement.
-- `itemstack` MUST be a compass.
-- Returns the itemstack of the magnetized compass.
nav.magnetize_compass = function(itemstack, magnet_pos, sound_pos, achievement_player)
itemstack:set_name("rp_nav:magnocompass_0")
local meta = itemstack:get_meta()
meta:set_int("magno_x", magnet_pos.x)
meta:set_int("magno_y", magnet_pos.y)
meta:set_int("magno_z", magnet_pos.z)
minetest.sound_play({name="rp_nav_magnetize_compass", gain=0.2}, {pos=sound_pos}, true)
if achievement_player then
achievements.trigger_achievement(achievement_player, "true_navigator_v2")
end
return itemstack
end
-- Demagnetize the compass item (`itemstack`) to point at North again,
-- playing a confirm sound at `sound_pos`.
-- Returns the itemstack of the demagnetized compass.
nav.demagnetize_compass = function(itemstack, sound_pos)
local is_magnocompass = minetest.get_item_group(itemstack:get_name(), "nav_compass") == 2
if not is_magnocompass then
-- Dont demagnetize if not magnocompass
return itemstack
end
itemstack:set_name("rp_nav:compass_0")
local meta = itemstack:get_meta()
meta:set_string("magno_x", "")
meta:set_string("magno_y", "")
meta:set_string("magno_z", "")
minetest.sound_play({name="rp_nav_demagnetize_compass", gain=0.2}, {pos=sound_pos}, true)
return itemstack
end
local compass_node_box = {
type = "fixed",
fixed = {
{-5/16, -0.5, -4/16, 5/16, -7/16, 4/16},
{-4/16, -0.5, 4/16, 4/16, -7/16, 5/16},
{-4/16, -0.5, -5/16, 4/16, -7/16, -4/16},
},
}
local compass_selection_box = {
type = "fixed",
fixed = {
{-5/16, -0.5, -5/16, 5/16, -7/16, 5/16},
},
}
for c=0,7 do
local magnetize_on_place = function(itemstack, placer, pointed_thing)
local handle_itemstack = itemstack
local is_magno = minetest.get_item_group(itemstack:get_name(), "nav_compass") == 2
if pointed_thing.type == "node" then
-- Change the itemstack for node placement so the correct
-- compass orientation is shown. Important for the rp_itemshow mod
local nodepos = pointed_thing.under
local node = minetest.get_node(nodepos)
-- If this group is set, compass needle always faces upwards
if minetest.get_item_group(node.name, "uses_canonical_compass") == 1 then
if is_magno then
handle_itemstack:set_name("rp_nav:magnocompass_0")
else
handle_itemstack:set_name("rp_nav:compass_0")
end
-- Otherwise, adjust the compass needle so it shows to the correct direction
else
local nodedef = minetest.registered_nodes[node.name]
local nodeyaw = 0
if nodedef and (nodedef.paramtype2 == "wallmounted" or nodedef.paramtype2 == "colorwallmounted") then
nodeyaw = minetest.dir_to_yaw(minetest.wallmounted_to_dir(node.param2))
elseif nodedef and (nodedef.paramtype2 == "facedir" or nodedef.paramtype2 == "colorfacedir") then
nodeyaw = minetest.dir_to_yaw(minetest.facedir_to_dir(node.param2))
end
-- Special case: Item frame. Add a little offset for nodepos as
-- the compass entity is at the side of the node rather than the center.
if node.name == "rp_itemshow:frame" then
local nodedir = minetest.facedir_to_dir(node.param2)
local offset = 7/16
nodepos = vector.add(nodepos, vector.multiply(nodedir, offset))
end
handle_itemstack = update_compass_itemstack(itemstack, nodepos, nodeyaw)
end
end
-- Handle pointed node handlers
local handled, handled_itemstack = util.on_place_pointed_node_handler(handle_itemstack, placer, pointed_thing)
if handled then
return handled_itemstack
end
-- Magnetize compass when placing on a magnetic node
-- (skip if 'sneak' key is pressed)
local check_magnet = true
if placer and placer:is_player() then
local ctrl = placer:get_player_control()
if ctrl.sneak then
check_magnet = false
end
end
if check_magnet then
local nodepos = pointed_thing.under
local node = minetest.get_node(nodepos)
-- demagnetize compass at magnetic node
if minetest.get_item_group(node.name, "magnetic") > 0 then
itemstack = nav.magnetize_compass(itemstack, nodepos, placer:get_pos(), placer)
return itemstack
-- demagnetize magnocompass at "unmagnetic" node
elseif is_magno and minetest.get_item_group(node.name, "unmagnetic") > 0 then
itemstack = nav.demagnetize_compass(itemstack, placer:get_pos())
return itemstack
end
end
-- Place compass as node
local place_in, place_floor = util.pointed_thing_to_place_pos(pointed_thing)
if place_in == nil then
return itemstack
end
-- Check protection
if minetest.is_protected(place_in, placer:get_player_name()) and
not minetest.check_player_privs(placer, "protection_bypass") then
minetest.record_protection_violation(pos, placer:get_player_name())
return itemstack
end
local place_item = handle_itemstack:get_name()
local itemmeta = handle_itemstack:get_meta()
local mx = itemmeta:get_int("magno_x")
local my = itemmeta:get_int("magno_y")
local mz = itemmeta:get_int("magno_z")
if is_magno then
local mpos = vector.new(mx, 0, mz)
local ppos = vector.new(place_in.x, 0, place_in.z)
if vector.distance(mpos, ppos) < 0.1 then
place_item = "rp_nav:magnocompass_rotating"
end
else
place_item = "rp_nav:compass_0"
end
local node_floor = minetest.get_node(place_floor)
local def_floor = minetest.registered_nodes[node_floor.name]
if (not def_floor) or (not def_floor.walkable) or minetest.get_item_group(node_floor.name, "attached_node") == 1 then
return itemstack
end
-- Place node
minetest.set_node(place_in, {name = place_item})
-- Set node metadata (magnocompass position)
if is_magno then
local nodemeta = minetest.get_meta(place_in)
local itemmeta = handle_itemstack:get_meta()
local mx = itemmeta:get_int("magno_x")
local my = itemmeta:get_int("magno_y")
local mz = itemmeta:get_int("magno_z")
nodemeta:set_int("magno_x", mx)
nodemeta:set_int("magno_y", my)
nodemeta:set_int("magno_z", mz)
end
-- Node sound
local idef = handle_itemstack:get_definition()
if idef and idef.sounds then
local snd = idef.sounds.place
if snd then
minetest.sound_play(snd, {pos = place_in}, true)
end
end
-- Reduce item count
if not minetest.is_creative_enabled(placer:get_player_name()) then
itemstack:take_item()
end
return itemstack
end
if c == 0 then
-- Compass 0 points to North and is a placable node
minetest.register_node(
"rp_nav:compass_0",
{
description = d,
_tt_help = t,
_rp_canonical_item = "rp_nav:compass_0",
drawtype = "nodebox",
walkable = false,
node_box = compass_node_box,
selection_box = compass_selection_box,
paramtype = "light",
sunlight_propagates = true,
tiles = {
node_imgs[c],
"("..node_imgs[c]..")^[transformR180",
"rp_nav_compass_side.png",
},
use_texture_alpha = "clip",
sounds = rp_sounds.node_sound_defaults(),
inventory_image = inv_imgs[c],
wield_image = wield_imgs[c],
node_placement_prediction = "",
on_place = magnetize_on_place,
floodable = true,
on_flood = function(pos, oldnode, newnode)
minetest.add_item(pos, "rp_nav:compass_0")
end,
groups = {nav_compass = 1, tool=1, attached_node=1, dig_immediate=3 },
stack_max = 1,
})
else
-- Compass 1 and higher points to the other cardinal directions.
-- They aren't nodes since the placed compass must always
-- point North.
minetest.register_craftitem(
"rp_nav:compass_"..c,
{
description = d,
_tt_help = t,
_rp_canonical_item = "rp_nav:compass_0",
inventory_image = inv_imgs[c],
wield_image = wield_imgs[c],
on_place = magnetize_on_place,
floodable = true,
on_flood = function(pos, oldnode, newnode)
minetest.add_item(pos, "rp_nav:compass_0")
end,
groups = {nav_compass = 1, tool=1, not_in_creative_inventory = 1 },
stack_max = 1,
})
end
local preserve_metadata_magnocompass = function(pos, oldnode, oldmeta, drops)
for d=1, #drops do
local item = drops[d]
if minetest.get_item_group(item:get_name(), "nav_compass") == 2 then
local mx = tonumber(oldmeta.magno_x) or 0
local my = tonumber(oldmeta.magno_y) or 0
local mz = tonumber(oldmeta.magno_z) or 0
local itemmeta = item:get_meta()
itemmeta:set_int("magno_x", mx)
itemmeta:set_int("magno_y", my)
itemmeta:set_int("magno_z", mz)
end
end
end
-- Magno compass, points to a position.
-- Unlike the normal compass, all magnocompass directions are
-- placable nodes since a magno compass might point to any position.
minetest.register_node(
"rp_nav:magnocompass_"..c,
{
description = dm,
_tt_help = tm,
_rp_canonical_item = "rp_nav:magnocompass_0",
drawtype = "nodebox",
walkable = false,
node_box = compass_node_box,
selection_box = compass_selection_box,
paramtype = "light",
sunlight_propagates = true,
tiles = {
node_imgs_magno[c],
"("..node_imgs_magno[c]..")^[transformR180",
"rp_nav_magnocompass_side.png",
},
use_texture_alpha = "clip",
sounds = rp_sounds.node_sound_defaults(),
inventory_image = inv_imgs_magno[c],
wield_image = wield_imgs_magno[c],
node_placement_prediction = "",
on_place = magnetize_on_place,
drop = "rp_nav:magnocompass_0",
floodable = true,
on_flood = function(pos, oldnode, newnode)
minetest.add_item(pos, "rp_nav:compass_0")
end,
preserve_metadata = preserve_metadata_magnocompass,
groups = {nav_compass = 2, tool=1, attached_node=1, dig_immediate=3, not_in_creative_inventory = 1 },
stack_max = 1,
})
-- Special magnocompass node if it is exactly (or nearly exactly) placed
-- at the magnetized X/Z position. The needle rotates in this special case.
minetest.register_node(
"rp_nav:magnocompass_rotating",
{
_rp_canonical_item = "rp_nav:magnocompass_0",
drawtype = "nodebox",
walkable = false,
node_box = compass_node_box,
selection_box = compass_selection_box,
paramtype = "light",
sunlight_propagates = true,
-- The rotating needle is a simple animation
tiles = {
{
name="rp_nav_magnocompass_rotating.png",
animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 1.6 }
},
{
name="rp_nav_magnocompass_rotating_below.png",
animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 1.6 }
},
"rp_nav_magnocompass_side.png",
},
use_texture_alpha = "clip",
sounds = rp_sounds.node_sound_defaults(),
inventory_image = "rp_nav_magnocompass_inventory_0.png",
wield_image = "rp_nav_magnocompass_inventory_0.png",
drop = "rp_nav:magnocompass_0",
preserve_metadata = preserve_metadata_magnocompass,
floodable = true,
on_flood = function(pos, oldnode, newnode)
minetest.add_item(pos, "rp_nav:compass_0")
end,
groups = {nav_compass = 2, tool=1, attached_node=1, dig_immediate=3, not_in_creative_inventory=1 },
stack_max = 1,
})
end
minetest.register_alias("rp_nav:compass", "rp_nav:compass_0")
minetest.register_alias("rp_nav:magnocompass", "rp_nav:magnocompass_0")
-- Crafting
crafting.register_craft(
{
output = "rp_nav:compass",
items = {
"rp_default:ingot_steel 4",
"rp_default:stick",
}
})
-- Achievements
achievements.register_achievement(
-- REFERENCE ACHIEVEMENT 5
"true_navigator_v2",
{
title = S("True Navigator"),
description = S("Magnetize a compass."),
times = 1,
item_icon = "rp_nav:magnocompass",
difficulty = 5,
})
minetest.register_alias("nav:compass", "rp_nav:compass_0")
for i=0, 7 do
minetest.register_alias("nav:compass_"..i, "rp_nav:compass_"..i)
end