VoxeLibre/mods/ENTITIES/mcl_minecarts/rails.lua

455 lines
12 KiB
Lua

local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
local mod = mcl_minecarts
local S = minetest.get_translator(modname)
mod.RAIL_GROUPS = {
STANDARD = 1,
CURVES = 2,
}
-- Inport functions and constants from elsewhere
local table_merge = mcl_util.table_merge
local check_connection_rules = mod.check_connection_rules
local update_rail_connections = mod.update_rail_connections
local minetest_fourdir_to_dir = minetest.fourdir_to_dir
local minetest_dir_to_fourdir = minetest.dir_to_fourdir
local vector_offset = vector.offset
local vector_equals = vector.equals
local north = mod.north
local south = mod.south
local east = mod.east
local west = mod.west
--- Rail direction Handleres
local function rail_dir_straight(pos, dir, node)
dir = vector.new(dir)
dir.y = 0
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_sloped(pos, dir, node)
local uphill = minetest_fourdir_to_dir(node.param2)
local downhill = minetest_fourdir_to_dir((node.param2+2)%4)
local up_uphill = vector_offset(uphill,0,1,0)
if vector_equals(dir, uphill) or vector_equals(dir, up_uphill) then
return up_uphill
else
return downhill
end
end
-- Fourdir to cardinal direction
-- 0 = north
-- 1 = east
-- 2 = south
-- 3 = west
-- This takes a table `dirs` that has one element for each cardinal direction
-- and which specifies the direction for a cart to continue in when entering
-- a rail node in the direction of the cardinal. This function takes node
-- rotations into account.
local function rail_dir_from_table(pos, dir, node, dirs)
dir = vector.new(dir)
dir.y = 0
local dir_fourdir = (minetest_dir_to_fourdir(dir) - node.param2 + 4) % 4
local new_fourdir = (dirs[dir_fourdir] + node.param2) % 4
return minetest_fourdir_to_dir(new_fourdir)
end
local CURVE_RAIL_DIRS = { [0] = 1, 1, 2, 2, }
local function rail_dir_curve(pos, dir, node)
return rail_dir_from_table(pos, dir, node, CURVE_RAIL_DIRS)
end
local function rail_dir_tee_off(pos, dir, node)
return rail_dir_from_table(pos, dir, node, CURVE_RAIL_DIRS)
end
local TEE_RAIL_ON_DIRS = { [0] = 0, 1, 1, 0 }
local function rail_dir_tee_on(pos, dir, node)
return rail_dir_from_table(pos, dir, node, TEE_RAIL_ON_DIRS)
end
local function rail_dir_cross(pos, dir, node)
dir = vector.new(dir)
dir.y = 0
-- Always continue in the same direction. No direction changes allowed
return dir
end
-- Setup shared text
local railuse = S(
"Place them on the ground to build your railway, the rails will automatically connect to each other and will"..
" turn into curves, T-junctions, crossings and slopes as needed."
)
mod.text = mod.text or {}
mod.text.railuse = railuse
local BASE_DEF = {
drawtype = "mesh",
mesh = "flat_track.obj",
paramtype = "light",
paramtype2 = "4dir",
stack_max = 64,
sounds = mcl_sounds.node_sound_metal_defaults(),
is_ground_content = true,
paramtype = "light",
use_texture_alpha = "clip",
collision_box = {
type = "fixed",
fixed = { -8/16, -8/16, -8/16, 8/16, -7/16, 8/15 }
},
selection_box = {
type = "fixed",
fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
},
groups = {
handy=1, pickaxey=1,
attached_node=1,
rail=1,
connect_to_raillike=minetest.raillike_group("rail"),
dig_by_water=0,destroy_by_lava_flow=0,
transport=1
},
description = S("New Rail"), -- Temporary name to make debugging easier
_tt_help = S("Track for minecarts"),
_doc_items_usagehelp = railuse,
_doc_items_longdesc = S("Rails can be used to build transport tracks for minecarts. Normal rails slightly slow down minecarts due to friction."),
on_place = function(itemstack, placer, pointed_thing)
local node_name = minetest.get_node(pointed_thing.under).name
-- Don't allow placing rail above rail
if minetest.get_item_group(node_name,"rail") == 0 then
return minetest.item_place_node(itemstack, placer, pointed_thing)
else
return itemstack
end
end,
after_place_node = function(pos, placer, itemstack, pointed_thing)
update_rail_connections(pos)
end,
_mcl_minecarts = {
get_next_dir = rail_dir_straight,
},
_mcl_blast_resistance = 0.7,
_mcl_hardness = 0.7,
}
local SLOPED_RAIL_DEF = table.copy(BASE_DEF)
table_merge(SLOPED_RAIL_DEF,{
drawtype = "mesh",
mesh = "sloped_track.obj",
groups = {
rail_slope = 1,
not_in_creative_inventory = 1,
},
collision_box = {
type = "fixed",
fixed = {
{ -0.5, -0.5, -0.5, 0.5, 0.0, 0.5 },
{ -0.5, 0.0, 0.0, 0.5, 0.5, 0.5 }
}
},
selection_box = {
type = "fixed",
fixed = {
{ -0.5, -0.5, -0.5, 0.5, 0.0, 0.5 },
{ -0.5, 0.0, 0.0, 0.5, 0.5, 0.5 }
}
},
_mcl_minecarts = {
get_next_dir = rail_dir_sloped,
},
})
function mod.register_rail(itemstring, ndef)
assert(ndef.tiles)
-- Extract out the craft recipe
local craft = ndef.craft
ndef.craft = nil
-- Add sensible defaults
if not ndef.inventory_image then ndef.inventory_image = ndef.tiles[1] end
if not ndef.wield_image then ndef.wield_image = ndef.tiles[1] end
--print("registering rail "..itemstring.." with definition: "..dump(ndef))
-- Make registrations
minetest.register_node(itemstring, ndef)
if craft then minetest.register_craft(craft) end
end
function mod.register_straight_rail(base_name, tiles, def)
def = def or {}
local base_def = table.copy(BASE_DEF)
local sloped_def = table.copy(SLOPED_RAIL_DEF)
local add = {
tiles = { tiles[1] },
drop = base_name,
groups = {
rail = mod.RAIL_GROUPS.STANDARD,
},
_mcl_minecarts = {
base_name = base_name,
can_slope = true,
},
}
table_merge(base_def, add); table_merge(sloped_def, add)
table_merge(base_def, def); table_merge(sloped_def, def)
-- Register the base node
mod.register_rail(base_name, base_def)
base_def.craft = nil; sloped_def.craft = nil
table_merge(base_def,{
_mcl_minecarts = {
railtype = "straight",
},
})
-- Sloped variant
mod.register_rail_sloped(base_name.."_sloped", table_merge(table.copy(sloped_def),{
_mcl_minecarts = {
get_next_dir = rail_dir_sloped,
},
mesecons = def.mesecons_sloped,
tiles = { tiles[1] },
_mcl_minecarts = {
railtype = "sloped",
},
}))
end
function mod.register_curves_rail(base_name, tiles, def)
def = def or {}
local base_def = table.copy(BASE_DEF)
local sloped_def = table.copy(SLOPED_RAIL_DEF)
local add = {
_mcl_minecarts = { base_name = base_name },
groups = {
rail = mod.RAIL_GROUPS.CURVES
},
drop = base_name,
}
table_merge(base_def, add); table_merge(sloped_def, add)
table_merge(base_def, def); table_merge(sloped_def, def)
-- Register the base node
mod.register_rail(base_name, table_merge(table.copy(base_def),{
tiles = { tiles[1] },
_mcl_minecarts = {
get_next_dir = rail_dir_straight,
railtype = "straight",
can_slope = true,
},
}))
-- Update for other variants
base_def.craft = nil
table_merge(base_def, {
groups = {
not_in_creative_inventory = 1
}
})
-- Corner variants
mod.register_rail(base_name.."_corner", table_merge(table.copy(base_def),{
tiles = { tiles[2] },
_mcl_minecarts = {
get_next_dir = rail_dir_curve,
railtype = "corner",
},
}))
-- Tee variants
mod.register_rail(base_name.."_tee_off", table_merge(table.copy(base_def),{
tiles = { tiles[3] },
mesecons = {
effector = {
action_on = function(pos, node)
local new_node = {name = base_name.."_tee_on", param2 = node.param2}
minetest.swap_node(pos, new_node)
end,
rules = mesecon.rules.alldirs,
}
},
_mcl_minecarts = {
get_next_dir = rail_dir_tee_off,
railtype = "tee",
},
}))
mod.register_rail(base_name.."_tee_on", table_merge(table.copy(base_def),{
tiles = { tiles[4] },
_mcl_minecarts = {
get_next_dir = rail_dir_tee_on,
railtype = "tee",
},
mesecons = {
effector = {
action_off = function(pos, node)
local new_node = {name = base_name.."_tee_off", param2 = node.param2}
minetest.swap_node(pos, new_node)
end,
rules = mesecon.rules.alldirs,
}
}
}))
-- Sloped variant
mod.register_rail_sloped(base_name.."_sloped", table_merge(table.copy(sloped_def),{
description = S("Sloped Rail"), -- Temporary name to make debugging easier
_mcl_minecarts = {
get_next_dir = rail_dir_sloped,
railtype = "tee",
},
tiles = { tiles[1] },
}))
-- Cross variant
mod.register_rail(base_name.."_cross", table_merge(table.copy(base_def),{
tiles = { tiles[5] },
_mcl_minecarts = {
get_next_dir = rail_dir_cross,
railtype = "cross",
},
}))
end
function mod.register_rail_sloped(itemstring, def)
assert(def.tiles)
-- Build the node definition
local ndef = table.copy(SLOPED_RAIL_DEF)
table_merge(ndef, def)
-- Add sensible defaults
if not ndef.inventory_image then ndef.inventory_image = ndef.tiles[1] end
if not ndef.wield_image then ndef.wield_image = ndef.tiles[1] end
--print("registering sloped rail "..itemstring.." with definition: "..dump(ndef))
-- Make registrations
minetest.register_node(itemstring, ndef)
end
-- Redstone rules
mod.rail_rules_long =
{{x=-1, y= 0, z= 0, spread=true},
{x= 1, y= 0, z= 0, spread=true},
{x= 0, y=-1, z= 0, spread=true},
{x= 0, y= 1, z= 0, spread=true},
{x= 0, y= 0, z=-1, spread=true},
{x= 0, y= 0, z= 1, spread=true},
{x= 1, y= 1, z= 0},
{x= 1, y=-1, z= 0},
{x=-1, y= 1, z= 0},
{x=-1, y=-1, z= 0},
{x= 0, y= 1, z= 1},
{x= 0, y=-1, z= 1},
{x= 0, y= 1, z=-1},
{x= 0, y=-1, z=-1}}
dofile(modpath.."/rails/normal.lua")
dofile(modpath.."/rails/activator.lua")
dofile(modpath.."/rails/detector.lua")
dofile(modpath.."/rails/powered.lua")
-- Aliases
if minetest.get_modpath("doc") then
doc.add_entry_alias("nodes", "mcl_minecarts:golden_rail", "nodes", "mcl_minecarts:golden_rail_on")
end
local CURVY_RAILS_MAP = {
["mcl_minecarts:rail"] = "mcl_minecarts:rail_v2",
}
for old,new in pairs(CURVY_RAILS_MAP) do
nodenames = mcl_util.table_keys(STRAIGHT_RAILS_MAP),
minetest.register_node(old, {
drawtype = "raillike",
inventory_image = new_def.inventory_image,
groups = { rail = 1 },
tiles = { new_def.tiles[1], new_def.tiles[1], new_def.tiles[1], new_def.tiles[1] },
})
end
minetest.register_lbm({
name = "mcl_minecarts:update_legacy_curvy_rails",
nodenames = mcl_util.table_keys(CURVY_RAILS_MAP),
action = function(pos, node)
node.name = CURVY_RAILS_MAP[node.name]
if node.name then
minetest.swap_node(pos, node)
mod.update_rail_connections(pos, { legacy = true, ignore_neighbor_connections = true })
end
end
})
local STRAIGHT_RAILS_MAP ={
["mcl_minecarts:golden_rail"] = "mcl_minecarts:golden_rail_v2",
["mcl_minecarts:golden_rail_on"] = "mcl_minecarts:golden_rail_v2_on",
["mcl_minecarts:activator_rail"] = "mcl_minecarts:activator_rail_v2",
["mcl_minecarts:activator_rail_on"] = "mcl_minecarts:activator_rail_v2_on",
["mcl_minecarts:detector_rail"] = "mcl_minecarts:detector_rail_v2",
["mcl_minecarts:detector_rail_on"] = "mcl_minecarts:detector_rail_v2_on",
}
for old,new in pairs(STRAIGHT_RAILS_MAP) do
local new_def = minetest.registered_nodes[new]
minetest.register_node(old, {
drawtype = "raillike",
inventory_image = new_def.inventory_image,
groups = { rail = 1 },
tiles = { new_def.tiles[1], new_def.tiles[1], new_def.tiles[1], new_def.tiles[1] },
})
end
local TRANSLATE_RAILS_MAP = table.copy(STRAIGHT_RAILS_MAP)
table_merge(TRANSLATE_RAILS_MAP, CURVY_RAILS_MAP)
minetest.register_lbm({
name = "mcl_minecarts:update_legacy_straight_rails",
nodenames = mcl_util.table_keys(STRAIGHT_RAILS_MAP),
run_at_every_load = true,
action = function(pos, node)
node.name = STRAIGHT_RAILS_MAP[node.name]
if node.name then
local connections = mod.get_rail_connections(pos, { legacy = true, ignore_neighbor_connections = true })
if not mod.HORIZONTAL_STANDARD_RULES[connections] then
-- Drop an immortal object at this location
local item_entity = minetest.add_item(pos, ItemStack(node.name))
if item_entity then
item_entity:get_luaentity()._immortal = true
end
-- This is a configuration that doesn't exist in the new rail
-- Replace with a standard rail
node.name = "mcl_minecarts:rail_v2"
end
minetest.swap_node(pos, node)
mod.update_rail_connections(pos, { legacy = true, ignore_neighbor_connections = true })
end
end
})
-- Convert old rail in the player's inventory to new rail
minetest.register_on_joinplayer(function(player)
local inv = player:get_inventory()
local size = inv:get_size("main")
for i=1,size do
local stack = inv:get_stack("main", i)
local new_name = TRANSLATE_RAILS_MAP[stack:get_name()]
if new_name then
stack:set_name(new_name)
inv:set_stack("main", i, stack)
end
end
end)