Change connection rules again to allow building parallel track, tees and crosses), start implementing rail rules callbacks

This commit is contained in:
teknomunk 2024-03-30 22:02:06 +00:00
parent 75f394e5ab
commit 34f394d8dc
1 changed files with 135 additions and 46 deletions

View File

@ -15,40 +15,55 @@ local function table_merge(base, overlay)
end end
local north = vector.new( 0, 0, 1); local N = 1 local north = vector.new( 0, 0, 1); local N = 1
local south = vector.new( 0, 0,-1); local S = 2 -- Note: this is overwritten below local south = vector.new( 0, 0,-1); local S = 2 -- Note: S is overwritten below with the translator
local east = vector.new( 1, 0, 0); local E = 4 local east = vector.new( 1, 0, 0); local E = 4
local west = vector.new(-1, 0, 0); local W = 8 local west = vector.new(-1, 0, 0); local W = 8
local HORIZONTAL_CONNECTIONS = { north, south, east, west } local CONNECTIONS = { north, south, east, west }
local HORIZONTAL_STANDARD_MAPPINGS = { local HORIZONTAL_STANDARD_RULES = {
[N] = { "", 0 }, [N] = { "", 0, mask = N, score = 1 },
[S] = { "", 0 }, [S] = { "", 0, mask = S, score = 1 },
[N+S] = { "", 0 }, [N+S] = { "", 0, mask = N+S, score = 2 },
[E] = { "", 1 }, [E] = { "", 1, mask = E, score = 1 },
[W] = { "", 1 }, [W] = { "", 1, mask = W, score = 1 },
[E+W] = { "", 1 }, [E+W] = { "", 1, mask = E+W, score = 2 },
} }
local HORIZONTAL_CURVES_MAPPINGS = {
[N+E] = { "_corner", 3 },
[N+W] = { "_corner", 2 },
[S+E] = { "_corner", 0 },
[S+W] = { "_corner", 1 },
[N+E+W] = { "_tee_off", 3 }, local HORIZONTAL_CURVES_RULES = {
[S+E+W] = { "_tee_off", 1 }, [N+E] = { "_corner", 3, name = "ne corner", mask = N+E, score = 3 },
[N+S+E] = { "_tee_off", 0 }, [N+W] = { "_corner", 2, name = "nw corner", mask = N+W, score = 3 },
[N+S+W] = { "_tee_off", 2 }, [S+E] = { "_corner", 0, name = "se corner", mask = S+E, score = 3 },
[S+W] = { "_corner", 1, name = "sw corner", mask = S+W, score = 3 },
-- [N+S+E+W] = "_cross", [N+E+W] = { "_tee_off", 3, mask = N+E+W, score = 4 },
[S+E+W] = { "_tee_off", 1, mask = S+E+W, score = 4 },
[N+S+E] = { "_tee_off", 0, mask = N+S+E, score = 4 },
[N+S+W] = { "_tee_off", 2, mask = N+S+W, score = 4 },
[N+S+E+W] = { "_cross", 0, mask = N+S+E+W, score = 5 },
} }
table_merge(HORIZONTAL_CURVES_MAPPINGS, HORIZONTAL_STANDARD_MAPPINGS)
local HORIZONTAL_MAPPINGS_BY_RAIL_GROUP = { table_merge(HORIZONTAL_CURVES_RULES, HORIZONTAL_STANDARD_RULES)
[1] = HORIZONTAL_STANDARD_MAPPINGS, local HORIZONTAL_RULES_BY_RAIL_GROUP = {
[2] = HORIZONTAL_CURVES_MAPPINGS, [1] = HORIZONTAL_STANDARD_RULES,
[2] = HORIZONTAL_CURVES_RULES,
} }
print(dump(HORIZONTAL_MAPPINGS_BY_RAIL_GROUP))
local DIRECTION_BITS = {N, S, E, W} local function check_connection_rule(pos, connections, rule)
-- All bits in the mask must be set for the connection to be possible
if bit.band(rule.mask,connections) ~= rule.mask then
--print("Mask mismatch ("..tostring(rule.mask)..","..tostring(connections)..")")
return false
end
-- If there is an allow filter, that mush also return true
if rule.allow and rule.allow(rule, connections, pos) then
return false
end
return true
end
local function update_rail_connections(pos, update_neighbors) local function update_rail_connections(pos, update_neighbors)
local node = minetest.get_node(pos) local node = minetest.get_node(pos)
@ -59,38 +74,105 @@ local function update_rail_connections(pos, update_neighbors)
end end
-- Get the mappings to use -- Get the mappings to use
local mappings = HORIZONTAL_MAPPINGS_BY_RAIL_GROUP[nodedef.groups.rail] local rules = HORIZONTAL_RULES_BY_RAIL_GROUP[nodedef.groups.rail]
if not mappings then return end if nodedef._mcl_minecarts and nodedef._mcl_minecarts.connection_rules then -- Custom connection rules
rules = nodedef._mcl_minecarts.connection_rules
end
if not rules then return end
-- Horizontal rules, Check for rails on each neighbor -- Horizontal rules, Check for rails on each neighbor
local connections = 0 local connections = 0
for i = 1,4 do for i,dir in ipairs(CONNECTIONS) do
local neighbor = vector.add(pos, HORIZONTAL_CONNECTIONS[i]) local neighbor = vector.add(pos, dir)
local node = minetest.get_node(neighbor) local node = minetest.get_node(neighbor)
local nodedef = minetest.registered_nodes[node.name] local nodedef = minetest.registered_nodes[node.name]
if nodedef.groups.rail then -- TODO: modify to only allow connections to the ends of rails (direction rules)
connections = connections + DIRECTION_BITS[i] if (nodedef.groups or {}).rail and nodedef._mcl_minecarts and nodedef._mcl_minecarts.get_next_dir then
local diff = vector.direction(neighbor, pos)
local next_dir = nodedef._mcl_minecarts.get_next_dir(neighbor, diff, node)
if next_dir == diff then
connections = connections + bit.lshift(1,i - 1)
end end
if update_neighbors then
update_rail_connections(neighbor, false)
end end
end end
local mapping = mappings[connections] -- Select the best allowed connection
if mapping then local rule = nil
local new_name = nodedef._mcl_minecarts.base_name..mapping[1] local score = 0
if new_name ~= node.name or node.param2 ~= mapping[2] then for k,r in pairs(rules) do
print("swapping "..node.name.." for "..new_name..","..tostring(mapping[2]).." at "..tostring(pos)) if check_connection_rule(pos, connections, r) then
if r.score > score then
--print("Best rule so far is "..dump(r))
score = r.score
rule = r
end
end
end
if not rule then return end
-- Apply the mapping
local new_name = nodedef._mcl_minecarts.base_name..rule[1]
if new_name ~= node.name or node.param2 ~= rule[2] then
print("swapping "..node.name.." for "..new_name..","..tostring(rule[2]).." at "..tostring(pos))
node.name = new_name node.name = new_name
node.param2 = mapping[2] node.param2 = rule[2]
minetest.swap_node(pos, node) minetest.swap_node(pos, node)
end end
if rule.after then
rule.after(rule, pos, connections)
end end
end end
mod.update_rail_connections = update_rail_connections mod.update_rail_connections = update_rail_connections
local function rail_dir_straight(pos, dir, node)
if node.param2 == 0 or node.param2 == 2 then
if vector.equals(dir, north) then
return north
else
return south
end
else
if vector.equals(dir,east) then
return east
else
return west
end
end
end
local function rail_dir_curve(pos, dir, node)
if node.param2 == 0 then
-- South and East
if vector.equals(dir, south) then return south end
if vector.equals(dir, north) then return east end
if vector.equals(dir, west) then return south end
if vector.equals(dir, east) then return east end
elseif node.param2 == 1 then
-- South and West
if vector.equals(dir, south) then return south end
if vector.equals(dir, north) then return west end
if vector.equals(dir, west) then return west end
if vector.equals(dir, east) then return south end
elseif node.param2 == 2 then
-- North and West
if vector.equals(dir, south) then return west end
if vector.equals(dir, north) then return north end
if vector.equals(dir, west) then return west end
if vector.equals(dir, east) then return north end
elseif node.param2 == 3 then
-- North and East
if vector.equals(dir, south) then return east end
if vector.equals(dir, north) then return north end
if vector.equals(dir, west) then return north end
if vector.equals(dir, east) then return east end
end
end
local function rail_dir_cross(pos, dir, node)
-- Always continue in the same direction. No direction changes allowed
return dir
end
-- Now get the translator after we have finished using S for other things -- Now get the translator after we have finished using S for other things
local S = minetest.get_translator(modname) local S = minetest.get_translator(modname)
local BASE_DEF = { local BASE_DEF = {
@ -119,12 +201,18 @@ local function register_curves_rail(base_name, tiles, def)
-- Register the base node -- Register the base node
mod.register_rail(base_name, table_merge(table.copy(base_def),{ mod.register_rail(base_name, table_merge(table.copy(base_def),{
tiles = { tiles[1] }, tiles = { tiles[1] },
_mcl_minecarts = {
get_next_dir = rail_dir_straight
}
})) }))
BASE_DEF.craft = nil BASE_DEF.craft = nil
-- Corner variants -- Corner variants
mod.register_rail(base_name.."_corner", table_merge(table.copy(base_def),{ mod.register_rail(base_name.."_corner", table_merge(table.copy(base_def),{
tiles = { tiles[2] }, tiles = { tiles[2] },
_mcl_minecarts = {
get_next_dir = rail_dir_curve,
},
groups = { groups = {
not_in_creative_inventory = 1, not_in_creative_inventory = 1,
}, },
@ -163,18 +251,19 @@ local function register_curves_rail(base_name, tiles, def)
})) }))
mod.register_rail_sloped(base_name.."_sloped", table_merge(table.copy(base_def),{ mod.register_rail_sloped(base_name.."_sloped", table_merge(table.copy(base_def),{
description = S("Sloped Rail"), -- Temporary name to make debugging easier description = S("Sloped Rail"), -- Temporary name to make debugging easier
_mcl_minecarts = {
get_next_dir = rail_dir_cross,
},
tiles = { tiles[1] }, tiles = { tiles[1] },
})) }))
-- Cross variant -- Cross variant
--[[
mod.register_rail(base_name.."_cross", table_merge(table.copy(base_def),{ mod.register_rail(base_name.."_cross", table_merge(table.copy(base_def),{
tiles = { tiles[4] }, tiles = { tiles[5] },
groups = { groups = {
not_in_creative_inventory = 1, not_in_creative_inventory = 1,
}, },
})) }))
]]
end end
mod.register_curves_rail = register_curves_rail mod.register_curves_rail = register_curves_rail
register_curves_rail("mcl_minecarts:rail_v2", { register_curves_rail("mcl_minecarts:rail_v2", {