2022-08-16 16:12:37 -04:00

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)