Michal Cieslakiewicz 98867c7cd8 minertools: fix incorrect mineralfinder stone indexing.
Signed-off-by: Michal Cieslakiewicz <michal.cieslakiewicz@wp.pl>
2018-10-07 15:29:16 +02:00

381 lines
12 KiB
Lua

--[[
Common functions for devices in this mod.
]]--
minertools = {}
-- *********
-- Constants
-- *********
-- colors
local msg_white = minetest.get_color_escape_sequence("#FFFFFF")
local msg_yellow = minetest.get_color_escape_sequence("#FFFF00")
local msg_warn = minetest.get_color_escape_sequence("#FF8080")
local msg_zero = minetest.get_color_escape_sequence("#00FFFF")
local msg_plus = minetest.get_color_escape_sequence("#FF00FF")
local msg_hot = minetest.get_color_escape_sequence("#FFC0C0")
local msg_cold = minetest.get_color_escape_sequence("#C0C0FF")
local msg_high = minetest.get_color_escape_sequence("#52D017")
local msg_medium = minetest.get_color_escape_sequence("#EAC117")
local msg_low = minetest.get_color_escape_sequence("#E56717")
-- mineral labels (for display)
local mineral_label = {}
mineral_label["default:stone_with_coal"] = "coal"
mineral_label["default:stone_with_iron"] = "iron"
mineral_label["default:stone_with_copper"] = "copper"
mineral_label["default:stone_with_tin"] = "tin"
mineral_label["default:stone_with_gold"] = "gold"
mineral_label["default:stone_with_mese"] = "mese"
mineral_label["default:stone_with_diamond"] = "diamond"
mineral_label["default:obsidian"] = "obsidian"
if minetest.get_modpath("moreores") then
mineral_label["moreores:mineral_silver"] = "silver"
mineral_label["moreores:mineral_mithril"] = "mithril"
end
-- public constants
minertools.head_vec = vector.new({x = 0, y = 1, z = 0})
minertools.dbl_click_us = 300 * 1000
-- ****************
-- Helper functions
-- ****************
-- sounds
local function play_beep_ok(player_name)
minetest.sound_play("minertools_beep_ok", {
to_player = player_name,
gain = 0.8,
})
end
local function play_beep_err(player_name)
minetest.sound_play("minertools_beep_err", {
to_player = player_name,
gain = 0.5,
})
end
local function play_scan(player_name)
minetest.sound_play("minertools_scan", {
to_player = player_name,
gain = 0.8,
})
end
local function play_click(player_name)
minetest.sound_play("minertools_click", {
to_player = player_name,
gain = 0.6,
})
end
local function play_pulse(player_name)
minetest.sound_play("minertools_pulse", {
to_player = player_name,
gain = 0.8,
})
end
-- check if node is of type that device can scan
-- (only nodes of natural origin can be analyzed properly)
local function is_mineral(name)
if name == nil then return false end
if minetest.get_item_group(name, "sand") > 0 then return true end
if minetest.get_item_group(name, "soil") > 0 then return true end
if minetest.get_item_group(name, "stone") > 0 then return true end
if string.match(name, "^default:stone_with_") then return true end
if name == "default:gravel"
or name == "default:clay" then return true end
if minetest.get_modpath("moreores") then
if string.match(name, "^moreores:mineral_") then
return true
end
end
return false
end
-- check if node is made of obsidian
local function has_obsidian(name)
if name == nil then return false end
if string.match(name, "^default:obsidian") then return true end
if minetest.get_modpath("stairs") then -- part of minetest game now
if string.match(name, "^stairs:stair_obsidian") or
string.match(name, "^stairs:slab_obsidian") then
return true
end
end
return false
end
-- ************
-- Calculations
-- ************
-- calculate relative temperature (aka gradient based on thermal sources nearby)
-- return: temperature gradient
local function calculate_rel_temp(pos, radius)
local scan_vec = vector.new({x = radius, y = radius, z = radius})
local scan_pos1 = vector.subtract(pos, scan_vec)
local scan_pos2 = vector.add(pos, scan_vec)
local water = minetest.find_nodes_in_area(scan_pos1, scan_pos2,
{ "group:water" })
local lava = minetest.find_nodes_in_area(scan_pos1, scan_pos2,
{ "group:lava" })
local temp_var = 0.0
for _, v in ipairs(water) do
local vd = vector.distance(pos, v)
if vd <= radius then
temp_var = temp_var - 1 / ( vd * vd )
end
end
for _, v in ipairs(lava) do
local vd = vector.distance(pos, v)
if vd <= radius then
temp_var = temp_var + 1 / ( vd * vd )
end
end
return temp_var
end
-- directional scan for specified mineral
-- parameters: pos - device position (vector)
-- range - scan depth (float)
-- look_dir - direction of looking (normalized vector)
-- name - ore to search for (string)
-- return: mineral count, blocked by obsidian true/false, closest ore distance
local function dir_mineral_scan(pos, range, look_dir, name)
local node = {}
local pos_vec = {}
local last_vec = nil
local orecount = 0
local obsblock = false
local oredepth = 0
for i = 1, range, 1 do
pos_vec = vector.add(pos, vector.round(vector.multiply(look_dir, i)))
if not last_vec or not vector.equals(pos_vec, last_vec) then
node = minetest.get_node_or_nil(pos_vec)
if node then
if node.name == name then
orecount = orecount + 1
if oredepth == 0 then oredepth = i end
elseif has_obsidian(node.name) then
obsblock = true
break
end
end
last_vec = pos_vec -- protects against double count
end
end
return orecount, obsblock, oredepth
end
-- scan for ores in cubic area
-- parameters: pos - device position (vector)
-- range - scan max range (float)
-- nodes - node names to search for (array)
-- return: mineral table (keys - node names, values - node count)
local function area_mineral_scan(pos, range, nodes)
local scan_vec = vector.new({x = range, y = range, z = range})
local scan_pos1 = vector.subtract(pos, scan_vec)
local scan_pos2 = vector.add(pos, scan_vec)
local _, minerals = minetest.find_nodes_in_area(scan_pos1, scan_pos2, nodes)
return minerals
end
-- ***************
-- Display helpers
-- ***************
function minertools.print_mineral_type_set_to(label, player_name, stone_name)
minetest.chat_send_player(player_name,
msg_yellow .. "[" .. label .. "]" .. msg_white ..
" Mineral type set to " .. msg_zero ..
mineral_label[stone_name] .. msg_white)
end
function minertools.print_scan_range_set_to(label, player_name, scan_range)
minetest.chat_send_player(player_name,
msg_yellow .. "[" .. label .. "]" ..
msg_white .. " Scan range set to "
.. msg_zero .. scan_range .. msg_white)
end
function minertools.print_mineral_type_is_now(label, player_name, stone_name)
minetest.chat_send_player(player_name,
msg_yellow .. "[" .. label .. "]" .. msg_white ..
" Mineral type is now " .. msg_zero ..
mineral_label[stone_name] .. msg_white)
end
function minertools.print_scan_range_is_now(label, player_name, scan_range)
minetest.chat_send_player(player_name,
msg_yellow .. "[" .. label .. "]" ..
msg_white .. " Scan range is now "
.. msg_zero .. scan_range .. msg_white)
end
-- *******
-- Modules
-- *******
-- geothermometer
-- parameters: label - device name in chat (string)
-- player_name - owner of device (string)
-- node_pos - pointed node position (vector)
-- radius - area of heat calculation (number)
-- scale - scaling factor (number)
-- temp_fmt - format of display (string, default "%+.4f")
function minertools.geothermometer_use(label, player_name, node_pos,
radius, scale, temp_fmt)
if not is_mineral(minetest.get_node(node_pos).name) then
play_beep_err(player_name)
return nil
end
local strfmt = "%+.4f"
if temp_fmt then strfmt = temp_fmt end
local temp_var = calculate_rel_temp(node_pos, radius)
play_beep_ok(player_name)
local msg_val_clr = msg_white
if temp_var < 0 then msg_val_clr = msg_cold
elseif temp_var > 0 then msg_val_clr = msg_hot end
minetest.chat_send_player(player_name,
msg_yellow .. "[" .. label .. "]" .. msg_white ..
" Temperature gradient for this block is " ..
msg_val_clr .. string.format(strfmt, scale * temp_var) ..
msg_white)
return nil
end
-- mineral finder
-- parameters: label - device name in chat (string)
-- player_name - owner of device (string)
-- head_pos - player head position (vector)
-- look_dir - player looking direction (vector)
-- range - depth of scanning (number)
-- stone_name - name of ore node to search for (string)
-- disp_detail - show ore distance (0-none, 1-H/M/L, 2-percentage)
function minertools.mineralfinder_use(label, player_name, head_pos, look_dir,
range, stone_name, disp_detail)
if not mineral_label[stone_name] then return nil end
local rangeshow = 0
if disp_detail then rangeshow = disp_detail end
local orecount, obsblock, oredepth = dir_mineral_scan(head_pos,
range, look_dir,
stone_name)
local oremsg = ""
if orecount > 0 then oremsg = msg_plus
else oremsg = msg_zero end
oremsg = oremsg .. orecount
if oredepth > 0 then
if rangeshow == 1 then
oremsg = oremsg .. msg_white .. " (signal strength: "
if oredepth <= range / 3 then
oremsg = oremsg .. msg_high .. "HIGH"
elseif oredepth > range * 2 / 3 then
oremsg = oremsg .. msg_low .. "LOW"
else oremsg = oremsg .. msg_medium .. "MEDIUM" end
oremsg = oremsg .. msg_white .. ")"
elseif rangeshow == 2 then
oremsg = oremsg .. msg_white .. " (signal strength: "
local sigpct = 100.0 * (range - oredepth + 1) / range
if sigpct >= 75 then oremsg = oremsg .. msg_high
elseif sigpct < 25 then oremsg = oremsg .. msg_low
else oremsg = oremsg .. msg_medium end
oremsg = oremsg .. string.format("%d%%", sigpct) ..
msg_white .. ")"
end
end
if obsblock then
oremsg = oremsg .. msg_warn ..
" (warning - scan incomplete, blocked by obsidian)"
end
play_pulse(player_name)
minetest.chat_send_player(player_name,
msg_yellow .. "[" .. label .. "]" .. msg_white ..
" Scan result for " .. msg_zero ..
mineral_label[stone_name] .. msg_white ..
" : " .. oremsg .. msg_white)
return nil
end
-- mineral scanner
-- parameters: label - device name in chat (string)
-- player_name - owner of device (string)
-- player_pos - player position (vector)
-- range - cubic range of scanning (number)
-- stones_list - minerals to scan for (table)
function minertools.mineralscanner_use(label, player_name, player_pos,
range, stones_list)
local minerals = area_mineral_scan(player_pos, range, stones_list)
local oremsg = ""
for orenode, orecount in pairs(minerals) do
if mineral_label[orenode] then
local oms = mineral_label[orenode] .. " = " .. orecount
if orecount == 0 then oms = msg_zero .. oms
else oms = msg_plus .. oms end
if oremsg ~= "" then
oremsg = oremsg .. msg_white .. ", " .. oms
else oremsg = oms end
end
end
play_scan(player_name)
minetest.chat_send_player(player_name,
msg_yellow .. "[" .. label .. "]" .. msg_white ..
" Scan results for cubic range " .. range ..
" : " .. oremsg .. msg_white)
return nil
end
-- ************
-- Module modes
-- ************
-- mineral finder - change ore type
-- parameters: label - device name in chat (string)
-- player_name - owner of device (string)
-- stones_list - known minerals (table)
-- stone_index - current index in stones_list (number)
-- return: new ore index
function minertools.mineralfinder_switch_ore(label, player_name,
stones_list, stone_index)
play_click(player_name)
local new_stone_idx = (stone_index % #stones_list) + 1
minertools.print_mineral_type_set_to(label, player_name,
stones_list[new_stone_idx])
return new_stone_idx
end
-- mineral scanner - change scan range
-- parameters: label - device name in chat (string)
-- player_name - owner of device (string)
-- player_pos - player position (vector)
-- min_range - minimal range of scanning (number)
-- max_range - maximal range of scanning (number)
-- range - current range of scanning (number)
-- return: new range
function minertools.mineralscanner_switch_range(label, player_name,
min_range, max_range, range)
play_click(player_name)
local new_range = range - 1
if new_range < min_range then
new_range = max_range
end
minertools.print_scan_range_set_to(label, player_name, new_range)
return new_range
end
-- ****************
-- Computer helpers
-- ****************
-- play click and display new mode
function minertools.computer_mode_change_notify(label, player_name, mode_name)
play_click(player_name)
minetest.chat_send_player(player_name,
msg_yellow .. "[" .. label .. "]" .. msg_white ..
" Switching mode to " .. msg_yellow ..
mode_name .. msg_white)
return nil
end