208 lines
7.8 KiB
Lua
208 lines
7.8 KiB
Lua
|
|
railbuilder = {}
|
|
railbuilder_path = minetest.get_modpath("railbuilder")
|
|
|
|
advtrain_helpers = dofile(railbuilder_path.."/advtrain_helpers.lua");
|
|
tunnelmaker_helpers = dofile(railbuilder_path.."/tunnelmaker_helpers.lua");
|
|
|
|
dofile(railbuilder_path.."/railbuilder_datastore.lua");
|
|
dofile(railbuilder_path.."/railbuilder_ui.lua");
|
|
|
|
|
|
-- tries to find values for last_direction from the underlying node
|
|
local function try_initialize_last_direction(player, pos)
|
|
local player_data = railbuilder.datastore.get_data(player)
|
|
local node = minetest.get_node(pos)
|
|
player_data.railbuilder_last_direction, player_data.railbuilder_last_vertical_direction = advtrain_helpers.node_params_to_directions(node)
|
|
end
|
|
|
|
local function use_railbuilder_marker_tool(_, user, pointed_thing)
|
|
local player_data = railbuilder.datastore.get_data(user)
|
|
local target_pos = nil
|
|
|
|
if get_selected_position(user) then
|
|
target_pos = get_selected_position(user)
|
|
elseif false and pointed_thing.type == "nothing" then
|
|
local player_pos = vector.add(user:get_pos(),user:get_eye_offset())
|
|
local destination = vector.add(player_pos, vector.multiply(user:get_look_dir(), 50))
|
|
local hits = Raycast(player_pos, destination)
|
|
local thing = hits:next()
|
|
local hit = nil
|
|
while thing do
|
|
if thing.type == "node" then
|
|
hit = thing
|
|
break
|
|
end
|
|
thing = hits:next()
|
|
end
|
|
if hit == nil then
|
|
return
|
|
end
|
|
target_pos = hit.above
|
|
elseif pointed_thing.type == "node" then
|
|
local node = minetest.get_node(pointed_thing.under)
|
|
if advtrain_helpers.node_is_advtrains_rail(node) then
|
|
target_pos = pointed_thing.under
|
|
else
|
|
target_pos = pointed_thing.above
|
|
end
|
|
else
|
|
return
|
|
end
|
|
|
|
local did_try_build = false
|
|
local did_build = false
|
|
|
|
if is_start_marker_valid(user) then
|
|
did_try_build = true
|
|
remove_start_marker(user)
|
|
|
|
local railbuilder_start_pos = player_data.railbuilder_start_pos
|
|
local delta_pos = vector.subtract(target_pos, railbuilder_start_pos)
|
|
local direction_delta = delta_to_dir(delta_pos)
|
|
local can_build = can_build_rail(railbuilder_start_pos, target_pos)
|
|
|
|
if not tunnelmaker_helpers.is_supported_tunnelmaker_version() then
|
|
minetest.chat_send_player(user:get_player_name(), "Sorry for the confusion, but this version of tunnelmaker is not supported! Look at the documentation of railbuilder for information on how to fix this: https://github.com/rlars/railbuilder")
|
|
elseif can_build then
|
|
build_rail(user, railbuilder_start_pos, target_pos, true or delta_pos.y <= 0)
|
|
did_build = true
|
|
if delta_pos.y > 0 then
|
|
-- set end pos to previous position
|
|
end
|
|
player_data.railbuilder_last_direction = advtrain_helpers.direction_delta_to_advtrains_conn(vector.subtract(target_pos, railbuilder_start_pos))
|
|
player_data.railbuilder_last_vertical_direction = direction_delta.y
|
|
else
|
|
-- place a single rail if there is none already
|
|
if vector.equals(railbuilder_start_pos, target_pos) and player_data.railbuilder_last_direction ~= nil then
|
|
-- check target_pos and the one below if there is a rail
|
|
if not advtrain_helpers.is_advtrains_rail_at_pos_or_below(target_pos) then
|
|
minetest.set_node(target_pos, advtrain_helpers.direction_step_to_rail_params_sequence(advtrain_helpers.rotation_index_to_advtrains_dir(player_data.railbuilder_last_direction))())
|
|
end
|
|
end
|
|
player_data.railbuilder_last_direction = nil
|
|
end
|
|
end
|
|
|
|
-- eventually continue with showing marker for next track
|
|
if not did_try_build or did_build then
|
|
if not did_try_build then
|
|
try_initialize_last_direction(user, target_pos)
|
|
if advtrain_helpers.node_is_end_of_upper_slope(minetest.get_node(target_pos)) then
|
|
target_pos.y = target_pos.y + 1
|
|
end
|
|
end
|
|
player_data.railbuilder_start_pos = target_pos
|
|
set_start_marker(user, target_pos, direction_delta)
|
|
end
|
|
|
|
update_hud(user, true)
|
|
end
|
|
|
|
function update_railbuilder_callback(dtime)
|
|
|
|
for _, player in pairs(railbuilder.datastore.get_players()) do
|
|
if is_start_marker_lost(player) then
|
|
-- abort if start_pos was unloaded
|
|
remove_start_marker(player)
|
|
minetest.chat_send_player(player:get_player_name(), "Railbuilder: Start point is too far away, cancelling!")
|
|
update_hud(player, true)
|
|
else
|
|
update_hud(player, false)
|
|
end
|
|
end
|
|
end
|
|
|
|
function can_build_rail(start_pos, end_pos)
|
|
local valid_xz_ratios = { -2, -1, -0.5, 0.5, 1, 2 }
|
|
local valid_dy_ratios = { -0.5, -1/3, 0, 1/3, 0.5 }
|
|
local valid_xzy_ratios = { -0.5/math.sqrt(2), 0, 0.5/math.sqrt(2) }
|
|
|
|
local delta_pos = vector.subtract(end_pos, start_pos)
|
|
|
|
if delta_pos.x ~= 0 and delta_pos.z ~= 0 and table.indexof(valid_xz_ratios, delta_pos.x / delta_pos.z) ~= -1 then
|
|
if math.abs(delta_pos.x) == math.abs(delta_pos.z) then
|
|
xz = math.sqrt(delta_pos.x * delta_pos.x + delta_pos.z * delta_pos.z)
|
|
return table.indexof(valid_xzy_ratios, delta_pos.y/xz) ~= -1
|
|
end
|
|
-- diagonal with 30 or 60 degrees must have 0 slope
|
|
return delta_pos.y == 0
|
|
end
|
|
if delta_pos.x ~= 0 and delta_pos.z == 0 and table.indexof(valid_dy_ratios, delta_pos.y / delta_pos.x) ~= -1 then
|
|
return true
|
|
end
|
|
if delta_pos.x == 0 and delta_pos.z ~= 0 and table.indexof(valid_dy_ratios, delta_pos.y / delta_pos.z) ~= -1 then
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- build a rail with a fixed direction
|
|
function build_rail(player, start_pos, end_pos, build_last_node)
|
|
local delta_pos = vector.subtract(end_pos, start_pos)
|
|
local rail_node_params_seq = advtrain_helpers.direction_step_to_rail_params_sequence(delta_pos)
|
|
local direction_delta = delta_to_dir(delta_pos)
|
|
local current_pos = table.copy(start_pos)
|
|
current_pos.y = current_pos.y - 0.2 -- decrement a little bit, because y values will be rounded for slope placement
|
|
local tunnelmaker_horizontal_direction = advtrain_helpers.direction_delta_to_advtrains_conn(direction_delta)
|
|
|
|
-- skip first pos if there is already a rail
|
|
local start_on_rail = advtrain_helpers.is_advtrains_rail_at_pos_or_below(start_pos)
|
|
if advtrain_helpers.is_advtrains_rail_at_pos_or_below(start_pos) then
|
|
current_pos = vector.add(current_pos, direction_delta)
|
|
if direction_delta.y > 0 then
|
|
current_pos.y = current_pos.y - direction_delta.y
|
|
end
|
|
elseif direction_delta.y > 0 then
|
|
-- dont build last node if building up and we start with a slope
|
|
build_last_node = false
|
|
elseif direction_delta.y < 0 then
|
|
-- dont build first node if building down and we start with a slope
|
|
current_pos = vector.add(current_pos, direction_delta)
|
|
end
|
|
|
|
local is_first = true
|
|
|
|
while math.abs(current_pos.x - end_pos.x) > 1/2 or math.abs(current_pos.z - end_pos.z) > 1/2 do
|
|
tunnelmaker_vertical_direction = 0
|
|
if math.floor(current_pos.y) ~= math.floor(current_pos.y - direction_delta.y) then
|
|
tunnelmaker_vertical_direction = signum(direction_delta.y)
|
|
end
|
|
tunnelmaker_helpers.dig_tunnel(player, current_pos, tunnelmaker_horizontal_direction, tunnelmaker_vertical_direction)
|
|
minetest.set_node(current_pos, rail_node_params_seq())
|
|
if is_first then
|
|
advtrain_helpers.try_bend_rail_start(start_pos, direction_delta)
|
|
is_first = false
|
|
end
|
|
current_pos = vector.add(current_pos, direction_delta)
|
|
end
|
|
|
|
-- place last node only if building on same level or down, as the end point is the start of a ramp
|
|
if build_last_node then
|
|
tunnelmaker_helpers.dig_tunnel(player, current_pos, tunnelmaker_horizontal_direction, 0)
|
|
minetest.set_node(current_pos, rail_node_params_seq())
|
|
|
|
-- if only one rail is placed, then this was not called yet
|
|
if is_first then
|
|
-- bend start rail (if any)
|
|
advtrain_helpers.try_bend_rail_start(start_pos, direction_delta)
|
|
end
|
|
end
|
|
end
|
|
|
|
function on_use_callback(_, user, pointed_thing)
|
|
show_slope_selection_ui(user)
|
|
end
|
|
|
|
|
|
minetest.register_craftitem("railbuilder:trackmarker", {
|
|
description = "Move",
|
|
inventory_image = "railbuilder_trackmarker.png",
|
|
on_use = on_use_callback,
|
|
on_secondary_use = use_railbuilder_marker_tool,
|
|
on_place = use_railbuilder_marker_tool
|
|
|
|
})
|
|
|
|
minetest.register_globalstep(update_railbuilder_callback)
|