255 lines
9.6 KiB
Lua
255 lines
9.6 KiB
Lua
ADVTRAINS_RAILS_STRAIGHT2 = { "advtrains:dtrack_vst1", "advtrains:dtrack_vst2" }
|
|
ADVTRAINS_RAILS_STRAIGHT3 = { "advtrains:dtrack_vst31", "advtrains:dtrack_vst32", "advtrains:dtrack_vst33" }
|
|
ADVTRAINS_RAILS_DIAGONAL = { "advtrains:dtrack_vst1_45", "advtrains:dtrack_vst2_45" }
|
|
|
|
function fixed_atan(y, x)
|
|
local v = math.atan(y/x)
|
|
if y >= 0 and x < 0 then return v + math.pi end
|
|
if y < 0 and x < 0 then return v - math.pi end
|
|
return v
|
|
end
|
|
|
|
function signum(x)
|
|
if x < 0 then return -1 end
|
|
if x > 0 then return 1 end
|
|
return 0
|
|
end
|
|
|
|
-- calculate the slope of a given vector
|
|
function calc_slope(delta_pos)
|
|
if delta_pos.x == 0 then return delta_pos.y / math.abs(delta_pos.z) end
|
|
if delta_pos.z == 0 then return delta_pos.y / math.abs(delta_pos.x) end
|
|
return delta_pos.y / math.abs(math.sqrt(1/2 * (delta_pos.x * delta_pos.x + delta_pos.z * delta_pos.z)))
|
|
end
|
|
|
|
-- return a vector with the step width of 1 or 2 in x and z dimension and a fractional y dimension
|
|
function delta_to_dir(delta_pos)
|
|
local slope = calc_slope(delta_pos)
|
|
if delta_pos.x == 0 then return vector.new(0, slope, signum(delta_pos.z)) end
|
|
if delta_pos.z == 0 then return vector.new(signum(delta_pos.x), slope, 0) end
|
|
if math.abs(delta_pos.x) == 2 * math.abs(delta_pos.z) then
|
|
return vector.new(2 * signum(delta_pos.x), slope, signum(delta_pos.z))
|
|
end
|
|
if 2 * math.abs(delta_pos.x) == math.abs(delta_pos.z) then
|
|
return vector.new(signum(delta_pos.x), slope, 2 * signum(delta_pos.z))
|
|
end
|
|
return vector.new(signum(delta_pos.x), slope, signum(delta_pos.z))
|
|
end
|
|
|
|
local function node_is_advtrains_rail(node)
|
|
return string.match(node.name, "advtrains:dtrack")
|
|
end
|
|
|
|
local function is_advtrains_rail_at_pos_or_below(pos)
|
|
return node_is_advtrains_rail(minetest.get_node(pos)) or node_is_advtrains_rail(minetest.get_node(vector.subtract(pos, vector.new(0, 1, 0))))
|
|
end
|
|
|
|
-- returns advtrains connection index (a value from 0 to 15)
|
|
local function direction_delta_to_advtrains_conn(direction_delta)
|
|
return (math.floor(fixed_atan(direction_delta.x,direction_delta.z) / math.pi * 8 + 0.5)) % 16
|
|
end
|
|
|
|
-- create a list of steps that can be added when showing possible end points
|
|
-- calculate the direction depending on current player position
|
|
local function get_closest_directions(player, rail_start_pos)
|
|
local delta_pos = vector.subtract(player:get_pos(), rail_start_pos)
|
|
local rotation_index = direction_delta_to_advtrains_conn(delta_pos)
|
|
local out_directions = {}
|
|
for i=-1,1 do
|
|
table.insert(out_directions, (rotation_index + i) % 16)
|
|
end
|
|
return out_directions
|
|
end
|
|
|
|
-- generate a vector in the plain
|
|
-- rotation_index values -7 to 8
|
|
local function rotation_index_to_advtrains_dir(rotation_index)
|
|
local rotation_index_mod = rotation_index % 4
|
|
local rotation_index_whole = math.floor(rotation_index / 4)
|
|
|
|
local v = nil
|
|
if rotation_index_mod == 0 then
|
|
v = vector.new(0, 0, 1)
|
|
elseif rotation_index_mod == 1 then
|
|
v = vector.new(1, 0, 2)
|
|
elseif rotation_index_mod == 2 then
|
|
v = vector.new(1, 0, 1)
|
|
elseif rotation_index_mod == 3 then
|
|
v = vector.new(2, 0, 1)
|
|
end
|
|
local rot_v = vector.rotate(v, { x = 0, y = rotation_index_whole * math.pi / 2, z = 0 })
|
|
if rotation_index_whole == 1 or rotation_index_whole == 3 then
|
|
-- some bug in Minetest version 5.4 ...
|
|
return vector.subtract(vector.new(), rot_v)
|
|
end
|
|
return rot_v
|
|
end
|
|
|
|
-- generate a vector in 3D
|
|
-- rotation_index values -7 to 8
|
|
-- vertical_direction values in {- 1/2, -1/3, 0, 1/3, 1/2}
|
|
local function rotation_index_and_vertical_direction_to_advtrains_dir(rotation_index, vertical_direction)
|
|
local plain_dir = rotation_index_to_advtrains_dir(rotation_index)
|
|
if vertical_direction == 0 then
|
|
return plain_dir
|
|
elseif rotation_index % 4 == 0 then
|
|
-- straights have two possible inclinations
|
|
local multiplier = 1/math.abs(vertical_direction)
|
|
return vector.multiply( vector.add(plain_dir, vector.new(0, vertical_direction, 0)), multiplier)
|
|
elseif rotation_index % 4 == 2 then
|
|
return vector.multiply( vector.add(plain_dir, vector.new(0, signum(vertical_direction)*1/2, 0)), 2)
|
|
else
|
|
return nil
|
|
end
|
|
end
|
|
|
|
-- TODO: tried with rail_and_can_be_bent from advtrains/trackplacer.lua first, but this did not work - can we improve this?
|
|
-- returns possible directions starting from a given track at pos
|
|
local function get_advtrains_dirs(original_pos, last_horizontal_direction, last_vertical_direction)
|
|
local p_rails={}
|
|
local p_railpos={}
|
|
local node = minetest.get_node(original_pos)
|
|
if last_vertical_direction and last_vertical_direction ~= 0 then
|
|
table.insert(p_rails, last_horizontal_direction)
|
|
return p_rails
|
|
end
|
|
|
|
if not node or not node_is_advtrains_rail(node) then return nil end
|
|
|
|
local cconns=advtrains.get_track_connections(node.name, node.param2)
|
|
|
|
for _, conn in ipairs(cconns) do
|
|
table.insert(p_rails, conn.c)
|
|
end
|
|
|
|
if #p_rails == 2 then
|
|
local a = p_rails[1]
|
|
local b = p_rails[2]
|
|
local diff = (a-b)%8
|
|
local more_conns = nil
|
|
if diff == 0 then
|
|
more_conns = {(a-1) % 16, (a+1) % 16, (b-1) % 16, (b+1) % 16}
|
|
end
|
|
if diff == 7 then
|
|
more_conns = {(a+1) % 16, (a+2) % 16, (b-2) % 16, (b-1) % 16}
|
|
end
|
|
if diff == 1 then
|
|
more_conns = {(a-2) % 16, (a-1) % 16, (b+1) % 16, (b+2) % 16}
|
|
end
|
|
table.insert_all(p_rails, more_conns)
|
|
end
|
|
|
|
--if false then return p_rails end
|
|
--p_rails = {}
|
|
--tp = advtrains.trackplacer
|
|
--for i = 0, 15 do
|
|
-- pos = vector.add(original_pos, rotation_index_to_advtrains_dir(i))
|
|
-- if tp.rail_and_can_be_bent(pos, (i+8)%16) then
|
|
-- table.insert(p_rails, i)
|
|
-- end
|
|
--end
|
|
return p_rails
|
|
end
|
|
|
|
|
|
-- returns closure that generates item name and params to place a rail in the given direction
|
|
local function direction_step_to_rail_params_sequence(dir_step)
|
|
local rotation_index = direction_delta_to_advtrains_conn(dir_step) -- maps to values from [0 to 15]
|
|
local rotation_index_mod = rotation_index % 4
|
|
local rotation_index_whole = math.floor(rotation_index / 4)
|
|
if dir_step.y == 0 then
|
|
if rotation_index_mod == 0 then
|
|
rail_name = "advtrains:dtrack_st"
|
|
end
|
|
if rotation_index_mod == 1 then
|
|
rail_name = "advtrains:dtrack_st_30"
|
|
end
|
|
if rotation_index_mod == 2 then
|
|
rail_name = "advtrains:dtrack_st_45"
|
|
end
|
|
if rotation_index_mod == 3 then
|
|
rail_name = "advtrains:dtrack_st_60"
|
|
end
|
|
return function()
|
|
return { name=rail_name, param1=14, param2=rotation_index_whole }
|
|
end
|
|
else
|
|
local slope = calc_slope(dir_step)
|
|
if dir_step.y < 0 then
|
|
rotation_index_whole = (rotation_index_whole + 2) % 4
|
|
end
|
|
local rail_node_names = nil
|
|
if rotation_index_mod == 0 and math.abs(slope) == 0.5 then
|
|
rail_node_names = ADVTRAINS_RAILS_STRAIGHT2
|
|
elseif rotation_index_mod == 0 and math.abs(slope) == 1/3 then
|
|
rail_node_names = ADVTRAINS_RAILS_STRAIGHT3
|
|
elseif rotation_index_mod == 2 then
|
|
rail_node_names = ADVTRAINS_RAILS_DIAGONAL
|
|
end
|
|
local increment = signum(dir_step.y)
|
|
local rail_name_table_length = #rail_node_names
|
|
local i = (increment == -1 and rail_name_table_length - 1) or 0
|
|
return function()
|
|
local rail_name = rail_node_names[i + 1]
|
|
i = (i + increment) % rail_name_table_length
|
|
return { name=rail_name, param1=14, param2=rotation_index_whole }
|
|
end
|
|
end
|
|
end
|
|
|
|
local function try_bend_rail_start(start_pos, direction_delta)
|
|
if advtrains.trackplacer then
|
|
advtrains.trackplacer.bend_rail(vector.add(start_pos, direction_delta), (8 + advtrain_helpers.direction_delta_to_advtrains_conn(direction_delta)) % 16)
|
|
end
|
|
end
|
|
|
|
-- rturns a pair of booleans indicating if the rails in the direction or its opposite are connected
|
|
local function find_already_connected(pos)
|
|
if advtrains.trackplacer then
|
|
return advtrains.trackplacer.find_already_connected(pos, direction)
|
|
end
|
|
return false, false
|
|
end
|
|
|
|
local function node_is_end_of_upper_slope(node)
|
|
return table.indexof(ADVTRAINS_RAILS_STRAIGHT2, node.name) == 2 or
|
|
table.indexof(ADVTRAINS_RAILS_STRAIGHT3, node.name) == 3 or
|
|
table.indexof(ADVTRAINS_RAILS_DIAGONAL, node.name) == 2
|
|
end
|
|
|
|
-- return horizontal direction in [0, 15] and vertical direction in {-1/2,-1/3,0,1/3,1/2}
|
|
local function node_params_to_directions(node)
|
|
local mod4offset = 0
|
|
local vertical_direction = 0
|
|
if node.name == "advtrains:dtrack_st_30" then mod4offset = 1
|
|
elseif node.name == "advtrains:dtrack_st_45" then mod4offset = 2
|
|
elseif node.name == "advtrains:dtrack_st_60" then mod4offset = 3
|
|
elseif table.indexof(ADVTRAINS_RAILS_STRAIGHT2, node.name) == 1 then vertical_direction = -1/2
|
|
elseif table.indexof(ADVTRAINS_RAILS_STRAIGHT2, node.name) == 2 then vertical_direction = 1/2
|
|
elseif table.indexof(ADVTRAINS_RAILS_STRAIGHT3, node.name) == 1 then vertical_direction = -1/3
|
|
elseif table.indexof(ADVTRAINS_RAILS_STRAIGHT3, node.name) == 3 then vertical_direction = 1/3
|
|
elseif table.indexof(ADVTRAINS_RAILS_DIAGONAL, node.name) > 0 then
|
|
mod4offset = 2
|
|
if table.indexof(ADVTRAINS_RAILS_DIAGONAL, node.name) == 1 then vertical_direction = -1/2
|
|
else vertical_direction = 1/2
|
|
end
|
|
end
|
|
-- invert direction if lower end of ramp
|
|
if vertical_direction < 0 then mod4offset = mod4offset + 8 end
|
|
return (node.param2 * 4 + mod4offset) % 16, vertical_direction
|
|
end
|
|
|
|
return {
|
|
node_is_advtrains_rail = node_is_advtrains_rail,
|
|
is_advtrains_rail_at_pos_or_below = is_advtrains_rail_at_pos_or_below,
|
|
direction_delta_to_advtrains_conn = direction_delta_to_advtrains_conn,
|
|
get_closest_directions = get_closest_directions,
|
|
rotation_index_to_advtrains_dir = rotation_index_to_advtrains_dir,
|
|
rotation_index_and_vertical_direction_to_advtrains_dir = rotation_index_and_vertical_direction_to_advtrains_dir,
|
|
get_advtrains_dirs = get_advtrains_dirs,
|
|
direction_step_to_rail_params_sequence = direction_step_to_rail_params_sequence,
|
|
try_bend_rail_start = try_bend_rail_start,
|
|
find_already_connected = find_already_connected,
|
|
node_is_end_of_upper_slope = node_is_end_of_upper_slope,
|
|
node_params_to_directions = node_params_to_directions,
|
|
} |