355 lines
12 KiB
Lua
355 lines
12 KiB
Lua
-- HUD stuff and marker entities
|
|
|
|
-- first_person_offset from API is 0...
|
|
local FIRST_PERSON_EYE_OFFSET = vector.new(0, 1.5, 0)
|
|
|
|
local SelectionEntity = {
|
|
conquer = true,
|
|
initial_properties = {
|
|
physical = false,
|
|
collide_with_objects = false,
|
|
pointable = false,
|
|
collisionbox = {-0.5, 0.05, -0.5, 0.5, 0.1, 0.5},
|
|
visual_size = {x = 8, y = 2 },
|
|
mesh = "conquer_selection_disc.obj",
|
|
textures = { "conquer_selection_disc.png" },
|
|
visual = "mesh",
|
|
static_save = false,
|
|
use_texture_alpha = true,
|
|
},
|
|
}
|
|
|
|
function SelectionEntity:set_color(color, ratio)
|
|
self.object:set_properties({
|
|
textures = {
|
|
("conquer_selection_disc.png^[colorize:%s:%d"):format(color, ratio or 200),
|
|
},
|
|
})
|
|
end
|
|
|
|
function SelectionEntity:on_activate()
|
|
self.object:set_armor_groups({ immortal = 1 })
|
|
end
|
|
|
|
function SelectionEntity:on_detach()
|
|
self.object:remove()
|
|
end
|
|
|
|
|
|
local function show_possible_endpoints(player, pos, color)
|
|
return player:hud_add {
|
|
hud_elem_type = "image_waypoint",
|
|
name = name,
|
|
scale={x=4,y=4},
|
|
text = ("conquer_selection_disc.png^[colorize:%s:%d"):format(color, ratio or 200),
|
|
world_pos = pos,
|
|
z_index = -300,
|
|
}
|
|
end
|
|
|
|
|
|
local function draw_debug_point(player, pos, color)
|
|
return player:hud_add {
|
|
hud_elem_type = "image_waypoint",
|
|
name = name,
|
|
scale={x=0.5,y=0.5},
|
|
text = ("conquer_selection_disc.png^[colorize:%s:%d"):format(color, ratio or 200),
|
|
world_pos = pos,
|
|
z_index = -300,
|
|
}
|
|
end
|
|
|
|
-- for vectors p, q, v, w find scalars r, s so that p + r*v - (q + s*w) takes the smallest value possible
|
|
local function min_distance_lines(p, v, q, w)
|
|
local a = vector.dot(v, v)
|
|
local b = vector.dot(v, w)
|
|
local c = vector.dot(v, vector.subtract(q, p))
|
|
local d = vector.dot(v, w)
|
|
local e = vector.dot(w, w)
|
|
local f = vector.dot(w, vector.subtract(q, p))
|
|
return (b*f-c*e)/(b*d-a*e),(a*f-c*d)/(b*d-a*e)
|
|
end
|
|
|
|
-- for vectors p, q, v, w find scalars r, s so that p + r*v - (q + s*w) takes the smallest value possible
|
|
-- return p + r*v, q + s*w
|
|
local function closest_points_on_lines(p, v, q, w)
|
|
local r, s = min_distance_lines(p, v, q, w)
|
|
return vector.add( p, vector.multiply(v, r) ), vector.add( q, vector.multiply(w, s) )
|
|
end
|
|
|
|
local function trace_player_view(player, directions)
|
|
local player_data = railbuilder.datastore.get_data(player)
|
|
local start_pos = player_data.railbuilder_start_pos
|
|
if start_pos and directions then
|
|
local min_dist_found = 1E99
|
|
local min_dist_point = vector.new(0, 0, 0)
|
|
local first_person_eye_offset, third_person_offset = player:get_eye_offset()
|
|
first_person_eye_offset = FIRST_PERSON_EYE_OFFSET
|
|
eye_pos = vector.add(player:get_pos(), first_person_eye_offset)
|
|
look_dir = player:get_look_dir()
|
|
for _, direction in ipairs(directions) do
|
|
local dir_vector = advtrain_helpers.rotation_index_and_vertical_direction_to_advtrains_dir(direction, player_data.railbuilder_desired_vertical_direction or 0)
|
|
if dir_vector then
|
|
-- hud marker is at the center of the node
|
|
local p, q = closest_points_on_lines(start_pos, dir_vector, eye_pos, look_dir)
|
|
local r, s = min_distance_lines(start_pos, dir_vector, eye_pos, look_dir)
|
|
|
|
dist = vector.length( vector.subtract(q, p) )
|
|
if r > 0 and s > 0 and dist < min_dist_found then
|
|
min_dist_found = dist
|
|
min_dist_point = p
|
|
end
|
|
end
|
|
end
|
|
if player_data.ui.hud_track_preview_points.selected ~= nil then
|
|
player:hud_change(player_data.ui.hud_track_preview_points.selected.id, "text", ("conquer_selection_disc.png^[colorize:%s:%d"):format("#44FF44", ratio or 200))
|
|
player_data.ui.hud_track_preview_points.selected = nil
|
|
end
|
|
for _, point in ipairs(player_data.ui.hud_track_preview_points) do
|
|
if vector.distance(point.pos, min_dist_point) < 0.5 then
|
|
player:hud_change(point.id, "text", ("conquer_selection_disc.png^[colorize:%s:%d"):format("#FF0000", ratio or 200))
|
|
player_data.ui.hud_track_preview_points.selected = point
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function length_2d(v)
|
|
return math.sqrt(v.x * v.x + v.z * v.z)
|
|
end
|
|
|
|
local function update_hud_track_preview_points(player, rail_start_pos, directions)
|
|
|
|
local player_data = railbuilder.datastore.get_data(player)
|
|
|
|
player_data.ui.hud_update_last_player_pos = player:get_pos()
|
|
|
|
if #directions == 0 then return end
|
|
|
|
local delta_pos = vector.subtract(player:get_pos(), rail_start_pos)
|
|
local distance_to_start_pos_2d = length_2d(delta_pos)
|
|
local normalized_delta_2d = vector.normalize(vector.new(delta_pos.x, 0, delta_pos.z))
|
|
|
|
for _, direction in ipairs(directions) do
|
|
dir_step = advtrain_helpers.rotation_index_and_vertical_direction_to_advtrains_dir(direction, player_data.railbuilder_desired_vertical_direction or 0)
|
|
if dir_step then
|
|
if distance_to_start_pos_2d < 5 or vector.dot(normalized_delta_2d, dir_step) > 0 then
|
|
step_size = vector.length(dir_step)
|
|
end_i = 9 / step_size
|
|
step_multiplier = math.max(math.floor(distance_to_start_pos_2d / step_size - 1/2*end_i), 1)
|
|
for i=0,end_i do
|
|
point_pos = vector.add(rail_start_pos, vector.multiply(dir_step, step_multiplier + i))
|
|
table.insert(player_data.ui.hud_track_preview_points,
|
|
{
|
|
id = show_possible_endpoints(player, point_pos, "#44FF44"),
|
|
pos = point_pos
|
|
})
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function update_hud(player, force)
|
|
local player_data = railbuilder.datastore.get_data(player)
|
|
local start_pos = player_data.railbuilder_start_pos
|
|
|
|
if player:get_player_control().dig then
|
|
force = update_slope_selection_ui(player) or force
|
|
else
|
|
hide_slope_selection_ui(player)
|
|
end
|
|
|
|
-- nothing to update if player did not move
|
|
if force or vector.distance(player_data.ui.hud_update_last_player_pos, player:get_pos()) > 1 then
|
|
remove_hud_track_preview_points(player)
|
|
|
|
if is_start_marker_valid(player) then
|
|
local directions = advtrain_helpers.get_advtrains_dirs(start_pos, player_data.railbuilder_last_direction, player_data.railbuilder_last_vertical_direction) or
|
|
advtrain_helpers.get_closest_directions(player, start_pos)
|
|
update_hud_track_preview_points(player, start_pos, directions)
|
|
player_data.ui.hud_track_preview_directions = directions
|
|
end
|
|
end
|
|
if is_start_marker_valid(player) then
|
|
trace_player_view(player, player_data.ui.hud_track_preview_directions)
|
|
end
|
|
end
|
|
|
|
local function remove_hud_points(player, hud_points)
|
|
for _, hud_point in ipairs(hud_points) do
|
|
player:hud_remove(hud_point.id)
|
|
end
|
|
end
|
|
|
|
function remove_hud_track_preview_points(player)
|
|
local player_data = railbuilder.datastore.get_data(player)
|
|
if #player_data.ui.hud_track_preview_points > 0 then
|
|
remove_hud_points(player, player_data.ui.hud_track_preview_points)
|
|
player_data.ui.hud_track_preview_points = {}
|
|
end
|
|
end
|
|
|
|
function highlight_point(player, directions)
|
|
-- deselect last marked
|
|
if last_marked_node then
|
|
end
|
|
end
|
|
|
|
function get_selected_position(player)
|
|
local player_data = railbuilder.datastore.get_data(player)
|
|
if player_data.ui.hud_track_preview_points.selected then
|
|
return player_data.ui.hud_track_preview_points.selected.pos
|
|
end
|
|
return nil
|
|
end
|
|
|
|
|
|
local function draw_slope_selection_point(player, pos, color)
|
|
return player:hud_add {
|
|
hud_elem_type = "image_waypoint",
|
|
name = name,
|
|
scale={x=2,y=2},
|
|
text = ("conquer_selection_disc.png^[colorize:%s:%d"):format(color, ratio or 200),
|
|
world_pos = pos,
|
|
z_index = -300,
|
|
}
|
|
end
|
|
|
|
local function init_slope_selection_ui(player)
|
|
local player_data = railbuilder.datastore.get_data(player)
|
|
-- store initial look_dir
|
|
player_data.ui.slope_selection_initial_look_yaw = player:get_look_horizontal()
|
|
player_data.ui.slope_selection_initial_look_pitch = player:get_look_vertical()
|
|
player_data.ui.slope_selection_is_showing = true
|
|
end
|
|
|
|
|
|
local function slope_to_enum(slope)
|
|
if slope == -1/2 then return 1
|
|
elseif slope == -1/3 then return 2
|
|
elseif slope == 1/3 then return 3
|
|
elseif slope == 1/2 then return 4
|
|
end
|
|
return 0
|
|
end
|
|
|
|
-- update the nth point
|
|
local function update_slope_selection_nth_point(player, n)
|
|
local player_data = railbuilder.datastore.get_data(player)
|
|
for i, point in ipairs(player_data.ui.hud_slope_selection_points) do
|
|
if i == n then
|
|
player:hud_change(point.id, "text", ("conquer_selection_disc.png^[colorize:%s:%d"):format("#FF0000", ratio or 200))
|
|
else
|
|
player:hud_change(point.id, "text", ("conquer_selection_disc.png^[colorize:%s:%d"):format("#FFFFFF", ratio or 200))
|
|
end
|
|
end
|
|
end
|
|
|
|
function show_slope_selection_ui(player)
|
|
init_slope_selection_ui(player)
|
|
local player_data = railbuilder.datastore.get_data(player)
|
|
local start_pos = player_data.railbuilder_start_pos
|
|
if start_pos then
|
|
table.insert(player_data.ui.hud_slope_selection_points,
|
|
{
|
|
id = draw_slope_selection_point(player, vector.add(start_pos, vector.new(0, -0.5, 0)), "#FFFFFF"),
|
|
pos = point_pos
|
|
})
|
|
table.insert(player_data.ui.hud_slope_selection_points,
|
|
{
|
|
id = draw_slope_selection_point(player, vector.add(start_pos, vector.new(0, -1/3, 0)), "#FFFFFF"),
|
|
pos = point_pos
|
|
})
|
|
table.insert(player_data.ui.hud_slope_selection_points,
|
|
{
|
|
id = draw_slope_selection_point(player, vector.add(start_pos, vector.new(0, 1/3, 0)), "#FFFFFF"),
|
|
pos = point_pos
|
|
})
|
|
table.insert(player_data.ui.hud_slope_selection_points,
|
|
{
|
|
id = draw_slope_selection_point(player, vector.add(start_pos, vector.new(0, 0.5, 0)), "#FFFFFF"),
|
|
pos = point_pos
|
|
})
|
|
end
|
|
end
|
|
|
|
|
|
-- update the hud points for slope selection
|
|
-- return true if selected slope changed
|
|
function update_slope_selection_ui(player)
|
|
local player_data = railbuilder.datastore.get_data(player)
|
|
if not player_data.ui.slope_selection_is_showing then
|
|
return
|
|
end
|
|
local current_yaw = player:get_look_horizontal()
|
|
local current_pitch = player:get_look_vertical()
|
|
|
|
local desired_vertical_direction = player_data.railbuilder_desired_vertical_direction
|
|
|
|
-- abort if too much to the side
|
|
local yaw_diff_abs = math.abs(player_data.ui.slope_selection_initial_look_yaw - current_yaw)
|
|
if yaw_diff_abs < 0.3 or yaw_diff_abs > 2 * math.pi - 0.3 then
|
|
local pitch_diff = player_data.ui.slope_selection_initial_look_pitch - current_pitch
|
|
if pitch_diff < -0.15 then
|
|
desired_vertical_direction = -1/2
|
|
elseif pitch_diff < -0.07 then
|
|
desired_vertical_direction = -1/3
|
|
elseif pitch_diff < 0.07 then
|
|
desired_vertical_direction = 0
|
|
elseif pitch_diff < 0.15 then
|
|
desired_vertical_direction = 1/3
|
|
else
|
|
desired_vertical_direction = 1/2
|
|
end
|
|
end
|
|
|
|
if player_data.railbuilder_desired_vertical_direction ~= desired_vertical_direction then
|
|
player_data.railbuilder_desired_vertical_direction = desired_vertical_direction
|
|
update_slope_selection_nth_point(player, slope_to_enum(desired_vertical_direction))
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
|
|
function hide_slope_selection_ui(player)
|
|
local player_data = railbuilder.datastore.get_data(player)
|
|
if #player_data.ui.hud_slope_selection_points > 0 then
|
|
remove_hud_points(player, player_data.ui.hud_slope_selection_points)
|
|
player_data.ui.hud_slope_selection_points = {}
|
|
end
|
|
player_data.ui.slope_selection_is_showing = false
|
|
end
|
|
|
|
function set_start_marker(player, position, direction_delta)
|
|
local player_data = railbuilder.datastore.get_data(player)
|
|
local railbuilder_start_marker_pos = vector.new(position.x, position.y, position.z)
|
|
-- for positive slope, move the start marker a little bit down
|
|
if direction_delta and direction_delta.y > 0 then railbuilder_start_marker_pos.y = railbuilder_start_marker_pos.y - direction_delta.y end
|
|
player_data.ui.railbuilder_start_marker = minetest.add_entity(railbuilder_start_marker_pos, "railbuilder:selection")
|
|
end
|
|
|
|
function remove_start_marker(player)
|
|
local player_data = railbuilder.datastore.get_data(player)
|
|
|
|
local old_start_marker = player_data.ui.railbuilder_start_marker
|
|
|
|
player_data.ui.railbuilder_start_marker = nil
|
|
old_start_marker:remove()
|
|
end
|
|
|
|
function is_start_marker_valid(player)
|
|
local player_data = railbuilder.datastore.get_data(player)
|
|
return player_data.ui.railbuilder_start_marker and player_data.ui.railbuilder_start_marker:get_luaentity() ~= nil
|
|
end
|
|
|
|
-- returns true if the start marker exists but is unloaded because it is too far away
|
|
function is_start_marker_lost(player)
|
|
local player_data = railbuilder.datastore.get_data(player)
|
|
return player_data.ui.railbuilder_start_marker and not player_data.ui.railbuilder_start_marker:get_luaentity()
|
|
end
|
|
|
|
minetest.register_entity("railbuilder:selection", SelectionEntity)
|
|
|