Begin major rework of track registration system

This commit is contained in:
orwell96 2023-09-04 19:34:47 +02:00
parent 7ca8ac8d00
commit 950d6f640c
17 changed files with 2469 additions and 2176 deletions

View File

@ -18,8 +18,6 @@ advtrains.register_wagon(name, prototype, description, inventory_image)
# Wagon prototype properties
{
... all regular luaentity properties (mesh, textures, collisionbox a.s.o)...
drives_on = {default=true},
^- used to define the tracktypes (see below) that wagon can drive on. The tracktype identifiers are given as keys, similar to privileges)
max_speed = 10,
^- optional, default 10: defines the maximum speed this wagon can drive. The maximum speed of a train is determined by the wagon with the lowest max_speed value.
seats = {

View File

@ -21,7 +21,7 @@ minetest.register_tool("advtrains:copytool", {
local node=minetest.get_node_or_nil(pointed_thing.under)
if not node then atprint("[advtrains]Ignore at placer position") return itemstack end
local nodename=node.name
if(not advtrains.is_track_and_drives_on(nodename, {default=true})) then
if(not advtrains.is_track(nodename)) then
atprint("no track here, not placing.")
return itemstack
end

View File

@ -294,7 +294,7 @@ end
-- Going from the rail at pos (does not need to be rounded) along connection with id conn_idx, if there is a matching rail, return it and the matching connid
-- returns: <adjacent pos>, <conn index of adjacent>, <my conn index>, <railheight of adjacent>
-- parameter this_conns_p is connection table of this rail and is optional, is determined by get_rail_info_at if not provided.
function advtrains.get_adjacent_rail(this_posnr, this_conns_p, conn_idx, drives_on)
function advtrains.get_adjacent_rail(this_posnr, this_conns_p, conn_idx)
local this_pos = advtrains.round_vector_floor_y(this_posnr)
local this_conns = this_conns_p
local _
@ -318,11 +318,11 @@ function advtrains.get_adjacent_rail(this_posnr, this_conns_p, conn_idx, drives_
adj_pos.y = adj_pos.y + 1
end
local nextnode_ok, nextconns, nextrail_y=advtrains.get_rail_info_at(adj_pos, drives_on)
local nextnode_ok, nextconns, nextrail_y=advtrains.get_rail_info_at(adj_pos)
if not nextnode_ok then
adj_pos.y = adj_pos.y - 1
conn_y = conn_y + 1
nextnode_ok, nextconns, nextrail_y=advtrains.get_rail_info_at(adj_pos, drives_on)
nextnode_ok, nextconns, nextrail_y=advtrains.get_rail_info_at(adj_pos)
if not nextnode_ok then
return nil
end

View File

@ -268,17 +268,17 @@ end
--false if it's not a rail or the train does not drive on this rail, but it is loaded or
--nil if the node is neither loaded nor in trackdb
--the distraction between false and nil will be needed only in special cases.(train initpos)
function advtrains.get_rail_info_at(pos, drives_on)
function advtrains.get_rail_info_at(pos)
local rdp=advtrains.round_vector_floor_y(pos)
local node=ndb.get_node_or_nil(rdp)
if not node then return end
local nodename=node.name
if(not advtrains.is_track_and_drives_on(nodename, drives_on)) then
if(not advtrains.is_track(nodename)) then
return false
end
local conns, railheight, tracktype=advtrains.get_track_connections(node.name, node.param2)
local conns, railheight = advtrains.get_track_connections(node.name, node.param2)
return true, conns, railheight
end

751
advtrains/oldtracks.lua Normal file
View File

@ -0,0 +1,751 @@
--advtrains by orwell96, see readme.txt
--dev-time settings:
--EDIT HERE
--If the old non-model rails on straight tracks should be replaced by the new...
--false: no
--true: yes
advtrains.register_replacement_lbms=false
--[[TracksDefinition
nodename_prefix
texture_prefix
description
common={}
straight={}
straight45={}
curve={}
curve45={}
lswitchst={}
lswitchst45={}
rswitchst={}
rswitchst45={}
lswitchcr={}
lswitchcr45={}
rswitchcr={}
rswitchcr45={}
vert1={
--you'll probably want to override mesh here
}
vert2={
--you'll probably want to override mesh here
}
]]--
advtrains.all_tracktypes={}
--definition preparation
local function conns(c1, c2, r1, r2) return {{c=c1, y=r1}, {c=c2, y=r2}} end
local function conns3(c1, c2, c3, r1, r2, r3) return {{c=c1, y=r1}, {c=c2, y=r2}, {c=c3, y=r3}} end
advtrains.ap={}
advtrains.ap.t_30deg_flat={
regstep=1,
variant={
st={
conns = conns(0,8),
desc = "straight",
tpdouble = true,
tpsingle = true,
trackworker = "cr",
},
cr={
conns = conns(0,7),
desc = "curve",
tpdouble = true,
trackworker = "swlst",
},
swlst={
conns = conns3(0,8,7),
desc = "left switch (straight)",
trackworker = "swrst",
switchalt = "cr",
switchmc = "on",
switchst = "st",
switchprefix = "swl",
},
swlcr={
conns = conns3(0,7,8),
desc = "left switch (curve)",
trackworker = "swrcr",
switchalt = "st",
switchmc = "off",
switchst = "cr",
switchprefix = "swl",
},
swrst={
conns = conns3(0,8,9),
desc = "right switch (straight)",
trackworker = "st",
switchalt = "cr",
switchmc = "on",
switchst = "st",
switchprefix = "swr",
},
swrcr={
conns = conns3(0,9,8),
desc = "right switch (curve)",
trackworker = "st",
switchalt = "st",
switchmc = "off",
switchst = "cr",
switchprefix = "swr",
},
},
regtp=true,
tpdefault="st",
trackworker={
["swrcr"]="st",
["swrst"]="st",
["cr"]="swlst",
["swlcr"]="swrcr",
["swlst"]="swrst",
},
rotation={"", "_30", "_45", "_60"},
}
advtrains.ap.t_yturnout={
regstep=1,
variant={
l={
conns = conns3(0,7,9),
desc = "Y-turnout (left)",
switchalt = "r",
switchmc = "off",
switchst = "l",
switchprefix = "",
},
r={
conns = conns3(0,9,7),
desc = "Y-turnout (right)",
switchalt = "l",
switchmc = "on",
switchst = "r",
switchprefix = "",
}
},
regtp=true,
tpdefault="l",
rotation={"", "_30", "_45", "_60"},
}
advtrains.ap.t_s3way={
regstep=1,
variant={
l={
conns = { {c=0}, {c=7}, {c=8}, {c=9}, {c=0} },
desc = "3-way turnout (left)",
switchalt = "s",
switchst="l",
switchprefix = "",
},
s={
conns = { {c=0}, {c=8}, {c=7}, {c=9}, {c=0} },
desc = "3-way turnout (straight)",
switchalt ="r",
switchst = "s",
switchprefix = "",
},
r={
conns = { {c=0}, {c=9}, {c=8}, {c=7}, {c=0} },
desc = "3-way turnout (right)",
switchalt = "l",
switchst="r",
switchprefix = "",
}
},
regtp=true,
tpdefault="l",
rotation={"", "_30", "_45", "_60"},
}
advtrains.ap.t_30deg_slope={
regstep=1,
variant={
vst1={conns = conns(8,0,0,0.5), rail_y = 0.25, desc = "steep uphill 1/2", slope=true},
vst2={conns = conns(8,0,0.5,1), rail_y = 0.75, desc = "steep uphill 2/2", slope=true},
vst31={conns = conns(8,0,0,0.33), rail_y = 0.16, desc = "uphill 1/3", slope=true},
vst32={conns = conns(8,0,0.33,0.66), rail_y = 0.5, desc = "uphill 2/3", slope=true},
vst33={conns = conns(8,0,0.66,1), rail_y = 0.83, desc = "uphill 3/3", slope=true},
},
regsp=true,
slopeplacer={
[2]={"vst1", "vst2"},
[3]={"vst31", "vst32", "vst33"},
max=3,--highest entry
},
slopeplacer_45={
[2]={"vst1_45", "vst2_45"},
max=2,
},
rotation={"", "_30", "_45", "_60"},
trackworker={},
increativeinv={},
}
advtrains.ap.t_30deg_straightonly={
regstep=1,
variant={
st={
conns = conns(0,8),
desc = "straight",
tpdouble = true,
tpsingle = true,
trackworker = "st",
},
},
regtp=true,
tpdefault="st",
rotation={"", "_30", "_45", "_60"},
}
advtrains.ap.t_30deg_straightonly_noplacer={
regstep=1,
variant={
st={
conns = conns(0,8),
desc = "straight",
tpdouble = true,
tpsingle = true,
trackworker = "st",
},
},
tpdefault="st",
rotation={"", "_30", "_45", "_60"},
}
advtrains.ap.t_45deg={
regstep=2,
variant={
st={
conns = conns(0,8),
desc = "straight",
tpdouble = true,
tpsingle = true,
trackworker = "cr",
},
cr={
conns = conns(0,6),
desc = "curve",
tpdouble = true,
trackworker = "swlst",
},
swlst={
conns = conns3(0,8,6),
desc = "left switch (straight)",
trackworker = "swrst",
switchalt = "cr",
switchmc = "on",
switchst = "st",
},
swlcr={
conns = conns3(0,6,8),
desc = "left switch (curve)",
trackworker = "swrcr",
switchalt = "st",
switchmc = "off",
switchst = "cr",
},
swrst={
conns = conns3(0,8,10),
desc = "right switch (straight)",
trackworker = "st",
switchalt = "cr",
switchmc = "on",
switchst = "st",
},
swrcr={
conns = conns3(0,10,8),
desc = "right switch (curve)",
trackworker = "st",
switchalt = "st",
switchmc = "off",
switchst = "cr",
},
},
regtp=true,
tpdefault="st",
trackworker={
["swrcr"]="st",
["swrst"]="st",
["cr"]="swlst",
["swlcr"]="swrcr",
["swlst"]="swrst",
},
rotation={"", "_30", "_45", "_60"},
}
advtrains.ap.t_perpcrossing={
regstep = 1,
variant={
st={
conns = { {c=0}, {c=8}, {c=4}, {c=12} },
desc = "perpendicular crossing",
tpdouble = true,
tpsingle = true,
trackworker = "st",
},
},
regtp=true,
tpdefault="st",
rotation={"", "_30", "_45", "_60"},
}
advtrains.ap.t_90plusx_crossing={
regstep = 1,
variant={
["30l"]={
conns = { {c=0}, {c=8}, {c=1}, {c=9} },
desc = "30/90 degree crossing (left)",
tpdouble = true,
tpsingle = true,
trackworker = "45l"
},
["45l"]={
conns = { {c=0}, {c=8}, {c=2}, {c=10} },
desc = "45/90 degree crossing (left)",
tpdouble = true,
tpsingle = true,
trackworker = "60l",
},
["60l"]={
conns = { {c=0}, {c=8}, {c=3}, {c=11}},
desc = "60/90 degree crossing (left)",
tpdouble = true,
tpsingle = true,
trackworker = "60r",
},
["60r"]={
conns = { {c=0}, {c=8}, {c=5}, {c=13} },
desc = "60/90 degree crossing (right)",
tpdouble = true,
tpsingle = true,
trackworker = "45r"
},
["45r"]={
conns = { {c=0}, {c=8}, {c=6}, {c=14} },
desc = "45/90 degree crossing (right)",
tpdouble = true,
tpsingle = true,
trackworker = "30r",
},
["30r"]={
conns = { {c=0}, {c=8}, {c=7}, {c=15}},
desc = "30/90 degree crossing (right)",
tpdouble = true,
tpsingle = true,
trackworker = "30l",
},
},
regtp=true,
tpdefault="30l",
rotation={""},
trackworker = {
["30l"] = "45l",
["45l"] = "60l",
["60l"] = "60r",
["60r"] = "45r",
["45r"] = "30r",
["30r"] = "30l",
}
}
advtrains.ap.t_diagonalcrossing = {
regstep=1,
variant={
["30l45r"]={
conns = {{c=1}, {c=9}, {c=6}, {c=14}},
desc = "30left-45right diagonal crossing",
tpdouble=true,
tpsingle=true,
trackworker="60l30l",
},
["60l30l"]={
conns = {{c=3}, {c=11}, {c=1}, {c=9}},
desc = "30left-60right diagonal crossing",
tpdouble=true,
tpsingle=true,
trackworker="60l45r"
},
["60l45r"]={
conns = {{c=3}, {c=11}, {c=6}, {c=14}},
desc = "60left-45right diagonal crossing",
tpdouble=true,
tpsingle=true,
trackworker="60l60r"
},
["60l60r"]={
conns = {{c=3}, {c=11}, {c=5}, {c=13}},
desc = "60left-60right diagonal crossing",
tpdouble=true,
tpsingle=true,
trackworker="60r45l",
},
--If 60l60r had a mirror image, it would be here, but it's symmetric.
-- 60l60r is also equivalent to 30l30r but rotated 90 degrees.
["60r45l"]={
conns = {{c=5}, {c=13}, {c=2}, {c=10}},
desc = "60right-45left diagonal crossing",
tpdouble=true,
tpsingle=true,
trackworker="60r30r",
},
["60r30r"]={
conns = {{c=5}, {c=13}, {c=7}, {c=15}},
desc = "60right-30right diagonal crossing",
tpdouble=true,
tpsingle=true,
trackworker="30r45l",
},
["30r45l"]={
conns = {{c=7}, {c=15}, {c=2}, {c=10}},
desc = "30right-45left diagonal crossing",
tpdouble=true,
tpsingle=true,
trackworker="30l45r",
},
},
regtp=true,
tpdefault="30l45r",
rotation={""},
trackworker = {
["30l45r"] = "60l30l",
["60l30l"] = "60l45r",
["60l45r"] = "60l60r",
["60l60r"] = "60r45l",
["60r45l"] = "60r30r",
["60r30r"] = "30r45l",
["30r45l"] = "30l45r",
}
}
advtrains.trackpresets = advtrains.ap
--definition format: ([] optional)
--[[{
nodename_prefix
texture_prefix
[shared_texture]
models_prefix
models_suffix (with dot)
[shared_model]
formats={
st,cr,swlst,swlcr,swrst,swrcr,vst1,vst2
(each a table with indices 0-3, for if to register a rail with this 'rotation' table entry. nil is assumed as 'all', set {} to not register at all)
}
common={} change something on common rail appearance
}
[18.12.17] Note on new connection system:
In order to support real rail crossing nodes and finally make the trackplacer respect switches, I changed the connection system.
There can be a variable number of connections available. These are specified as tuples {c=<connection>, y=<rely>}
The table "at_conns" consists of {<conn1>, <conn2>...}
the "at_rail_y" property holds the value that was previously called "railheight"
Depending on the number of connections:
2 conns: regular rail
3 conns: switch:
- when train passes in at conn1, will move out of conn2
- when train passes in at conn2 or conn3, will move out of conn1
4 conns: cross (or cross switch, depending on arrangement of conns):
- conn1 <> conn2
- conn3 <> conn4
]]
-- Notify the user if digging the rail is not allowed
local function can_dig_callback(pos, player)
local ok, reason = advtrains.can_dig_or_modify_track(pos)
if not ok and player then
minetest.chat_send_player(player:get_player_name(), attrans("This track can not be removed!") .. " " .. reason)
end
return ok
end
function advtrains.register_tracks(tracktype, def, preset)
advtrains.trackplacer.register_tracktype(def.nodename_prefix, preset.tpdefault)
if preset.regtp then
advtrains.trackplacer.register_track_placer(def.nodename_prefix, def.texture_prefix, def.description, def)
end
if preset.regsp then
advtrains.slope.register_placer(def, preset)
end
for suffix, var in pairs(preset.variant) do
for rotid, rotation in ipairs(preset.rotation) do
if not def.formats[suffix] or def.formats[suffix][rotid] then
local img_suffix = suffix..rotation
local ndef = advtrains.merge_tables({
description=def.description.."("..(var.desc or "any")..rotation..")",
drawtype = "mesh",
paramtype="light",
paramtype2="facedir",
walkable = false,
selection_box = {
type = "fixed",
fixed = {-1/2-1/16, -1/2, -1/2, 1/2+1/16, -1/2+2/16, 1/2},
},
mesh = def.shared_model or (def.models_prefix.."_"..img_suffix..def.models_suffix),
tiles = {def.shared_texture or (def.texture_prefix.."_"..img_suffix..".png"), def.second_texture},
groups = {
attached_node = advtrains.IGNORE_WORLD and 0 or 1,
advtrains_track=1,
["advtrains_track_"..tracktype]=1,
save_in_at_nodedb=1,
dig_immediate=2,
not_in_creative_inventory=1,
not_blocking_trains=1,
},
can_dig = can_dig_callback,
after_dig_node=function(pos)
advtrains.ndb.update(pos)
end,
after_place_node=function(pos)
advtrains.ndb.update(pos)
end,
at_nnpref = def.nodename_prefix,
at_suffix = suffix,
at_rotation = rotation,
at_rail_y = var.rail_y
}, def.common or {})
if preset.regtp then
ndef.drop = def.nodename_prefix.."_placer"
end
if preset.regsp and var.slope then
ndef.drop = def.nodename_prefix.."_slopeplacer"
end
--connections
ndef.at_conns = advtrains.rotate_conn_by(var.conns, (rotid-1)*preset.regstep)
local ndef_avt_table
if var.switchalt and var.switchst then
local switchfunc=function(pos, node, newstate)
newstate = newstate or var.switchalt -- support for 3 (or more) state switches
-- this code is only called from the internal setstate function, which
-- ensures that it is safe to switch the turnout
if newstate~=var.switchst then
advtrains.ndb.swap_node(pos, {name=def.nodename_prefix.."_"..(var.switchprefix or "")..newstate..rotation, param2=node.param2})
advtrains.invalidate_all_paths(pos)
end
end
ndef.on_rightclick = function(pos, node, player)
if advtrains.check_turnout_signal_protection(pos, player:get_player_name()) then
advtrains.setstate(pos, nil, node)
advtrains.log("Switch", player:get_player_name(), pos)
end
end
if var.switchmc then
ndef.mesecons = {effector = {
["action_"..var.switchmc] = function(pos, node)
advtrains.setstate(pos, nil, node)
end,
rules=advtrains.meseconrules
}}
end
ndef_avt_table = {
getstate = var.switchst,
setstate = switchfunc,
}
end
local adef={}
if def.get_additional_definiton then
adef=def.get_additional_definiton(def, preset, suffix, rotation)
end
ndef = advtrains.merge_tables(ndef, adef)
-- insert getstate/setstate functions after merging the additional definitions
if ndef_avt_table then
ndef.advtrains = advtrains.merge_tables(ndef.advtrains or {}, ndef_avt_table)
end
minetest.register_node(":"..def.nodename_prefix.."_"..suffix..rotation, ndef)
--trackplacer
if preset.regtp then
local tpconns = {conn1=ndef.at_conns[1].c, conn2=ndef.at_conns[2].c}
if var.tpdouble then
advtrains.trackplacer.add_double_conn(def.nodename_prefix, suffix, rotation, tpconns)
end
if var.tpsingle then
advtrains.trackplacer.add_single_conn(def.nodename_prefix, suffix, rotation, tpconns)
end
end
advtrains.trackplacer.add_worked(def.nodename_prefix, suffix, rotation, var.trackworker)
end
end
end
advtrains.all_tracktypes[tracktype]=true
end
function advtrains.is_track_and_drives_on(nodename, drives_on_p)
local drives_on = drives_on_p
if not drives_on then drives_on = advtrains.all_tracktypes end
local hasentry = false
for _,_ in pairs(drives_on) do
hasentry=true
end
if not hasentry then drives_on = advtrains.all_tracktypes end
if not minetest.registered_nodes[nodename] then
return false
end
local nodedef=minetest.registered_nodes[nodename]
for k,v in pairs(drives_on) do
if nodedef.groups["advtrains_track_"..k] then
return true
end
end
return false
end
function advtrains.get_track_connections(name, param2)
local nodedef=minetest.registered_nodes[name]
if not nodedef then atprint(" get_track_connections couldn't find nodedef for nodename "..(name or "nil")) return nil end
local noderot=param2
if not param2 then noderot=0 end
if noderot > 3 then atprint(" get_track_connections: rail has invaild param2 of "..noderot) noderot=0 end
local tracktype
for k,_ in pairs(nodedef.groups) do
local tt=string.match(k, "^advtrains_track_(.+)$")
if tt then
tracktype=tt
end
end
return advtrains.rotate_conn_by(nodedef.at_conns, noderot*AT_CMAX/4), (nodedef.at_rail_y or 0), tracktype
end
-- Function called when a track is about to be dug or modified by the trackworker
-- Returns either true (ok) or false,"translated string describing reason why it isn't allowed"
function advtrains.can_dig_or_modify_track(pos)
if advtrains.get_train_at_pos(pos) then
return false, attrans("Position is occupied by a train.")
end
-- interlocking: tcb, signal IP a.s.o.
if advtrains.interlocking then
-- TCB?
if advtrains.interlocking.db.get_tcb(pos) then
return false, attrans("There's a Track Circuit Break here.")
end
-- signal ip?
if advtrains.interlocking.db.is_ip_at(pos, true) then -- is_ip_at with purge parameter
return false, attrans("There's a Signal Influence Point here.")
end
end
return true
end
-- slope placer. Defined in register_tracks.
--crafted with rail and gravel
local sl={}
function sl.register_placer(def, preset)
minetest.register_craftitem(":"..def.nodename_prefix.."_slopeplacer",{
description = attrans("@1 Slope", def.description),
inventory_image = def.texture_prefix.."_slopeplacer.png",
wield_image = def.texture_prefix.."_slopeplacer.png",
groups={},
on_place = sl.create_slopeplacer_on_place(def, preset)
})
end
--(itemstack, placer, pointed_thing)
function sl.create_slopeplacer_on_place(def, preset)
return function(istack, player, pt)
if not pt.type=="node" then
minetest.chat_send_player(player:get_player_name(), attrans("Can't place: not pointing at node"))
return istack
end
local pos=pt.above
if not pos then
minetest.chat_send_player(player:get_player_name(), attrans("Can't place: not pointing at node"))
return istack
end
local node=minetest.get_node(pos)
if not minetest.registered_nodes[node.name] or not minetest.registered_nodes[node.name].buildable_to then
minetest.chat_send_player(player:get_player_name(), attrans("Can't place: space occupied!"))
return istack
end
if not advtrains.check_track_protection(pos, player:get_player_name()) then
minetest.record_protection_violation(pos, player:get_player_name())
return istack
end
--determine player orientation (only horizontal component)
--get_look_horizontal may not be available
local yaw=player.get_look_horizontal and player:get_look_horizontal() or (player:get_look_yaw() - math.pi/2)
--rounding unit vectors is a nice way for selecting 1 of 8 directions since sin(30°) is 0.5.
local dirvec={x=math.floor(math.sin(-yaw)+0.5), y=0, z=math.floor(math.cos(-yaw)+0.5)}
--translate to direction to look up inside the preset table
local param2, rot45=({
[-1]={
[-1]=2,
[0]=3,
[1]=3,
},
[0]={
[-1]=2,
[1]=0,
},
[1]={
[-1]=1,
[0]=1,
[1]=0,
},
})[dirvec.x][dirvec.z], dirvec.x~=0 and dirvec.z~=0
local lookup=preset.slopeplacer
if rot45 then lookup=preset.slopeplacer_45 end
--go unitvector forward and look how far the next node is
local step=1
while step<=lookup.max do
local node=minetest.get_node(vector.add(pos, dirvec))
--next node solid?
if not minetest.registered_nodes[node.name] or not minetest.registered_nodes[node.name].buildable_to or advtrains.is_protected(pos, player:get_player_name()) then
--do slopes of this distance exist?
if lookup[step] then
if minetest.settings:get_bool("creative_mode") or istack:get_count()>=step then
--start placing
local placenodes=lookup[step]
while step>0 do
minetest.set_node(pos, {name=def.nodename_prefix.."_"..placenodes[step], param2=param2})
if not minetest.settings:get_bool("creative_mode") then
istack:take_item()
end
step=step-1
pos=vector.subtract(pos, dirvec)
end
else
minetest.chat_send_player(player:get_player_name(), attrans("Can't place: Not enough slope items left (@1 required)", step))
end
else
minetest.chat_send_player(player:get_player_name(), attrans("Can't place: There's no slope of length @1",step))
end
return istack
end
step=step+1
pos=vector.add(pos, dirvec)
end
minetest.chat_send_player(player:get_player_name(), attrans("Can't place: no supporting node at upper end."))
return itemstack
end
end
advtrains.slope=sl
--END code, BEGIN definition
--definition format: ([] optional)
--[[{
nodename_prefix
texture_prefix
[shared_texture]
models_prefix
models_suffix (with dot)
[shared_model]
formats={
st,cr,swlst,swlcr,swrst,swrcr,vst1,vst2
(each a table with indices 0-3, for if to register a rail with this 'rotation' table entry. nil is assumed as 'all', set {} to not register at all)
}
common={} change something on common rail appearance
}]]

View File

@ -1,9 +1,5 @@
-- passive.lua
-- API to passive components, as described in passive_api.txt of advtrains_luaautomation
-- This has been moved to the advtrains core in turn with the interlocking system,
-- to prevent a dependency on luaautomation.
local deprecation_warned = {}
-- Rework for advtrains 2.5: The passive API now uses the reworked node_state system. Please see the comment in tracks.lua
function advtrains.getstate(parpos, pnode)
local pos
@ -19,20 +15,8 @@ function advtrains.getstate(parpos, pnode)
local node=pnode or advtrains.ndb.get_node(pos)
local ndef=minetest.registered_nodes[node.name]
local st
if ndef and ndef.advtrains and ndef.advtrains.getstate then
st=ndef.advtrains.getstate
elseif ndef and ndef.luaautomation and ndef.luaautomation.getstate then
if not deprecation_warned[node.name] then
minetest.log("warning", node.name.." uses deprecated definition of ATLATC functions in the 'luaautomation' field. Please move them to the 'advtrains' field!")
end
st=ndef.luaautomation.getstate
else
return nil
end
if type(st)=="function" then
return st(pos, node)
else
return st
if ndef and ndef.advtrains then
return ndef.advtrains.node_state
end
end
@ -45,31 +29,46 @@ function advtrains.setstate(parpos, newstate, pnode)
end
if type(pos)~="table" or (not pos.x or not pos.y or not pos.z) then
debug.sethook()
error("Invalid position supplied to getstate")
error("Invalid position supplied to setstate")
end
local node=pnode or advtrains.ndb.get_node(pos)
local ndef=minetest.registered_nodes[node.name]
local st
if ndef and ndef.advtrains and ndef.advtrains.setstate then
st=ndef.advtrains.setstate
elseif ndef and ndef.luaautomation and ndef.luaautomation.setstate then
if not deprecation_warned[node.name] then
minetest.log("warning", node.name.." uses deprecated definition of ATLATC functions in the 'luaautomation' field. Please move them to the 'advtrains' field!")
if not ndef or not ndef.advtrains then
return false, "missing_node_def"
end
st=ndef.luaautomation.setstate
else
return nil
local old_state = ndef.advtrains.node_state
if old_state == newstate then
-- nothing needs to be done
return true
end
if not ndef.advtrains.node_state_map then
return false, "missing_node_state_map"
end
local new_node_name = ndef.advtrains.node_state_map[newstate]
if not new_node_name then
return false, "no_such_state"
end
-- prevent state switching when route lock or train is present
if advtrains.get_train_at_pos(pos) then
return false
return false, "train_here"
end
if advtrains.interlocking and advtrains.interlocking.route.has_route_lock(minetest.pos_to_string(pos)) then
return false
return false, "route_lock_here"
end
-- perform the switch
local new_node = {name = new_node_name, param2 = node.param2}
advtrains.ndb.swap_node(pos, new_node)
-- if callback is present, call it
if ndef.advtrains.node_on_switch_state then
ndef.advtrains.node_on_switch_state(pos, new_node, old_state, newstate)
end
st(pos, node, newstate)
return true
end
@ -86,12 +85,7 @@ function advtrains.is_passive(parpos, pnode)
end
local node=pnode or advtrains.ndb.get_node(pos)
local ndef=minetest.registered_nodes[node.name]
if ndef and ndef.advtrains and ndef.advtrains.getstate then
return true
elseif ndef and ndef.luaautomation and ndef.luaautomation.getstate then
if not deprecation_warned[node.name] then
minetest.log("warning", node.name.." uses deprecated definition of ATLATC functions in the 'luaautomation' field. Please move them to the 'advtrains' field!")
end
if ndef and ndef.advtrains and ndef.advtrains.node_state_map then
return true
else
return false
@ -102,20 +96,10 @@ end
function advtrains.set_fallback_state(pos, pnode)
local node=pnode or advtrains.ndb.get_node(pos)
local ndef=minetest.registered_nodes[node.name]
local st
if ndef and ndef.advtrains and ndef.advtrains.setstate
and ndef.advtrains.fallback_state then
if advtrains.get_train_at_pos(pos) then
return false
if not ndef or not ndef.advtrains or not ndef.advtrains.node_fallback_state then
return false, "no_fallback_state"
end
if advtrains.interlocking and advtrains.interlocking.route.has_route_lock(minetest.pos_to_string(pos)) then
return false
end
ndef.advtrains.setstate(pos, node, ndef.advtrains.fallback_state)
return true
end
return advtrains.setstate(pos, ndef.advtrains.node_fallback_state, node)
end

View File

@ -33,13 +33,12 @@
-- If you need to proceed along the path by a specific actual distance, it does NOT work to simply add it to the index. You should use the path_get_index_by_offset() function.
-- creates the path data structure, reconstructing the train from a position and a connid
-- Important! train.drives_on must exist while calling this method
-- returns: true - successful
-- nil - node not yet available/unloaded, please wait
-- false - node definitely gone, remove train
function advtrains.path_create(train, pos, connid, rel_index)
local posr = advtrains.round_vector_floor_y(pos)
local node_ok, conns, rhe = advtrains.get_rail_info_at(pos, train.drives_on)
local node_ok, conns, rhe = advtrains.get_rail_info_at(pos)
if not node_ok then
return node_ok
end
@ -211,7 +210,7 @@ function advtrains.path_get(train, index)
if pef == train.path_trk_f then
node_ok, this_conns = advtrains.get_rail_info_at(pos)
if not node_ok then error("For train "..train.id..": Path item "..pef.." on-track but not a valid node!") end
adj_pos, adj_connid, conn_idx, nextrail_y, next_conns = advtrains.get_adjacent_rail(pos, this_conns, connid, train.drives_on)
adj_pos, adj_connid, conn_idx, nextrail_y, next_conns = advtrains.get_adjacent_rail(pos, this_conns, connid)
end
pef = pef + 1
if adj_pos then
@ -250,7 +249,7 @@ function advtrains.path_get(train, index)
if peb == train.path_trk_b then
node_ok, this_conns = advtrains.get_rail_info_at(pos)
if not node_ok then error("For train "..train.id..": Path item "..peb.." on-track but not a valid node!") end
adj_pos, adj_connid, conn_idx, nextrail_y, next_conns = advtrains.get_adjacent_rail(pos, this_conns, connid, train.drives_on)
adj_pos, adj_connid, conn_idx, nextrail_y, next_conns = advtrains.get_adjacent_rail(pos, this_conns, connid)
end
peb = peb - 1
if adj_pos then

View File

@ -40,9 +40,6 @@ local suppasp = {
for r,f in pairs({on={as="off", ls="green", als="red"}, off={as="on", ls="red", als="green"}}) do
advtrains.trackplacer.register_tracktype("advtrains:retrosignal", "")
advtrains.trackplacer.register_tracktype("advtrains:signal", "")
for rotid, rotation in ipairs({"", "_30", "_45", "_60"}) do
local crea=1
if rotid==1 and r=="off" then crea=0 end
@ -108,8 +105,8 @@ for r,f in pairs({on={as="off", ls="green", als="red"}, off={as="on", ls="red",
},
can_dig = can_dig_func,
after_dig_node = after_dig_func,
--TODO add rotation using trackworker
})
advtrains.trackplacer.add_worked("advtrains:retrosignal", r, rotation, nil)
minetest.register_node("advtrains:signal_"..r..rotation, {
drawtype = "mesh",
@ -179,8 +176,8 @@ for r,f in pairs({on={as="off", ls="green", als="red"}, off={as="on", ls="red",
},
can_dig = can_dig_func,
after_dig_node = after_dig_func,
--TODO add rotation using trackworker
})
advtrains.trackplacer.add_worked("advtrains:signal", r, rotation, nil)
end
local crea=1

View File

@ -3,311 +3,204 @@
--all new trackplacer code
local tp={
tracks={}
groups={}
}
function tp.register_tracktype(nnprefix, n_suffix)
if tp.tracks[nnprefix] then return end--due to the separate registration of slopes and flats for the same nnpref, definition would be overridden here. just don't.
tp.tracks[nnprefix]={
default=n_suffix,
single_conn={},
single_conn_1={},
single_conn_2={},
double_conn={},
double_conn_1={},
double_conn_2={},
--keys:conn1_conn2 (example:1_4)
--values:{name=x, param2=x}
twcycle={},
twrotate={},--indexed by suffix, list, tells order of rotations
modify={},
}
--[[ New in version 2.5:
The track placer no longer uses hacky nodename pattern matching.
The base criterion for rotating or matching tracks is the common "ndef.advtrains.track_place_group" property.
Only rails where this field is set are considered for replacement. Other rails can still be considered for connection.
Replacement ("bending") of rails can only happen within their respective track place group. Only two-conn rails are allowed in the trackplacer.
The track registration functions register the candidates for any given track_place_group in two separate collections:
- double: tracks that can be used to connect both ends of the rail
- single: tracks that will be used to connect conn1 when only a single end is to be connected
When track placing is requested, the calling code just supplies the track_place_group to be placed.
]]--
local function rotate(conn, rot)
return (conn + rot) % 16
end
function tp.add_double_conn(nnprefix, suffix, rotation, conns)
local nodename=nnprefix.."_"..suffix..rotation
-- Register a track node as candidate
-- tpg: the track place group to register the candidates for
-- name, ndef: the node name and node definition table to register
-- as_single: whether the rail should be considered as candidate for one-endpoint connection
-- Typically only set for the straight rail variants
-- as_double: whether the rail should be considered as candidate for two-endpoint connection
-- Typically set for straights and curves
function tp.register_candidate(tpg, name, ndef, as_single, as_double)
--get or create TP group
if not tp.groups[tpg] then
tp.groups[tpg] = {double = {}, single1 = {}, single2 = {}, default = {name = name, param2 = 0} }
-- note: this causes the first candidate to ever be registered to be the default (which is typically what you want)
end
local g = tp.groups[tpg]
-- get conns
assert(#ndef.at_conns == 2)
local c1, c2 = ndef.at_conns[1].c, ndef.at_conns[2].c
local is_symmetrical = (rotate(c1, 8) == c2)
-- store all possible rotations (param2 values)
for i=0,3 do
tp.tracks[nnprefix].double_conn[((conns.conn1+4*i)%16).."_"..((conns.conn2+4*i)%16)]={name=nodename, param2=i}
tp.tracks[nnprefix].double_conn[((conns.conn2+4*i)%16).."_"..((conns.conn1+4*i)%16)]={name=nodename, param2=i}
tp.tracks[nnprefix].double_conn_1[((conns.conn1+4*i)%16).."_"..((conns.conn2+4*i)%16)]={name=nodename, param2=i}
tp.tracks[nnprefix].double_conn_2[((conns.conn2+4*i)%16).."_"..((conns.conn1+4*i)%16)]={name=nodename, param2=i}
if as_double then
g.double[rotate(c1,i*4).."_"..rotate(c2,i*4)] = {name=name, param2=i}
if not is_symmmetrical then
g.double[rotate(c2,i*4).."_"..rotate(c1,i*4)] = {name=name, param2=i}
-- if the track is unsymmetric (e.g. a curve), we may require the "wrong" orientation to fill a gap.
end
tp.tracks[nnprefix].modify[nodename]=true
end
function tp.add_single_conn(nnprefix, suffix, rotation, conns)
local nodename=nnprefix.."_"..suffix..rotation
for i=0,3 do
tp.tracks[nnprefix].single_conn[((conns.conn1+4*i)%16)]={name=nodename, param2=i}
tp.tracks[nnprefix].single_conn[((conns.conn2+4*i)%16)]={name=nodename, param2=i}
tp.tracks[nnprefix].single_conn_1[((conns.conn1+4*i)%16)]={name=nodename, param2=i}
tp.tracks[nnprefix].single_conn_2[((conns.conn2+4*i)%16)]={name=nodename, param2=i}
if as_single then
g.single1[rotate(c1,i*4)] = {name=name, param2=i}
g.single2[rotate(c2,i*4)] = {name=name, param2=i}
end
end
tp.tracks[nnprefix].modify[nodename]=true
end
local function check_or_bend_rail(origin, dir, pname, commit)
local pos = advtrains.pos_add_dir(origin, dir)
local back_dir = advtrains.oppd(dir);
function tp.add_worked(nnprefix, suffix, rotation, cycle_follows)
tp.tracks[nnprefix].twcycle[suffix]=cycle_follows
if not tp.tracks[nnprefix].twrotate[suffix] then tp.tracks[nnprefix].twrotate[suffix]={} end
table.insert(tp.tracks[nnprefix].twrotate[suffix], rotation)
end
--[[
rewrite algorithm.
selection criteria: these will never be changed or even selected:
- tracks being already connected on both sides
- tracks that are already connected on one side but are not bendable to the desired position
the following situations can occur:
1. there are two more than two rails around
1.1 there is one or more subset(s) that can be directly connected
-> choose the first possibility
2.2 not
-> choose the first one and orient straight
2. there's exactly 1 rail around
-> choose and orient straight
3. there's no rail around
-> set straight
]]
local function istrackandbc(pos_p, conn)
local tpos = pos_p
local cnode=minetest.get_node(advtrains.dirCoordSet(tpos, conn.c))
if advtrains.is_track_and_drives_on(cnode.name, advtrains.all_tracktypes) then
local cconns=advtrains.get_track_connections(cnode.name, cnode.param2)
return advtrains.conn_matches_to(conn, cconns)
end
--try the same 1 node below
tpos = {x=tpos.x, y=tpos.y-1, z=tpos.z}
cnode=minetest.get_node(advtrains.dirCoordSet(tpos, conn.c))
if advtrains.is_track_and_drives_on(cnode.name, advtrains.all_tracktypes) then
local cconns=advtrains.get_track_connections(cnode.name, cnode.param2)
return advtrains.conn_matches_to(conn, cconns)
local node_ok, conns = advtrains.get_rail_info_at(pos)
if not node_ok then
-- try the node one level below
pos.y = pos.y - 1
node_ok, conns = advtrains.get_rail_info_at(pos)
end
if not node_ok then
return false
end
function tp.find_already_connected(pos)
local dnode=minetest.get_node(pos)
local dconns=advtrains.get_track_connections(dnode.name, dnode.param2)
local found_conn
for connid, conn in ipairs(dconns) do
if istrackandbc(pos, conn) then
if found_conn then --we found one in previous iteration
return true, true --signal that it's connected
else
found_conn = conn.c
-- if one conn of the node here already points towards us, nothing to do
for connid, conn in ipairs(conns) do
if back_dir == conn.c then
return true
end
end
end
return found_conn
end
function tp.rail_and_can_be_bent(originpos, conn)
local pos=advtrains.dirCoordSet(originpos, conn)
local newdir=(conn+8)%16
local node=minetest.get_node(pos)
if not advtrains.is_track_and_drives_on(node.name, advtrains.all_tracktypes) then
return false
end
-- can we bend the node here?
local node = advtrains.ndb.get_node(pos)
local ndef = minetest.registered_nodes[node.name]
local nnpref = ndef and ndef.at_nnpref
if not nnpref then return false end
local tr=tp.tracks[nnpref]
if not tr then return false end
if not tr.modify[node.name] then
--we actually can use this rail, but only if it already points to the desired direction.
if advtrains.is_track_and_drives_on(node.name, advtrains.all_tracktypes) then
local cconns=advtrains.get_track_connections(node.name, node.param2)
return advtrains.conn_matches_to(conn, cconns)
if not ndef or not ndef.advtrains or not ndef.advtrains.track_place_group then
return false
end
end
-- If the rail is not allowed to be modified, also only use if already in desired direction
-- now the track must be two-conn, else it wouldn't be allowed to have track_place_group set.
assert(#conns == 2)
-- Is player and game allowed to do this?
if not advtrains.can_dig_or_modify_track(pos) then
local cconns=advtrains.get_track_connections(node.name, node.param2)
return advtrains.conn_matches_to(conn, cconns)
end
--rail at other end?
local adj1, adj2=tp.find_already_connected(pos)
if adj1 and adj2 then
return false--dont destroy existing track
elseif adj1 and not adj2 then
if tr.double_conn[adj1.."_"..newdir] then
return true--if exists, connect new rail and old end
end
return false
else
if tr.single_conn[newdir] then--just rotate old rail to right orientation
end
if not advtrains.check_track_protection(pos, pname) then
return false
end
-- we confirmed that track can be modified. Does there exist a suitable connection candidate?
-- check if there are any unbound ends
local bound_connids = {}
for connid, conn in ipairs(conns) do
local adj_pos, adj_connid = advtrains.get_adjacent_rail(pos, conns, connid)
if adj_pos then
bound_connids[#bound_connids+1] = connid
end
end
-- depending on the nummber of ends, decide
if #bound_connids == 2 then
-- rail is within a fixed track, do not break up
return false
end
-- obtain the group table
local g = tp.groups[ndef.advtrains.track_place_group]
if #bound_connids == 1 then
-- we can attempt double
local bound_dir = conns[bound_connids[1]].c
if g.double[back_dir.."_"..bound_dir] then
if commit then
advtrains.ndb.swap_node(pos, g.double[back_dir.."_"..bound_dir])
end
return true
end
return false
else
-- rail is entirely unbound, we can attempt single1
if g.single1[back_dir] then
if commit then
advtrains.ndb.swap_node(pos, g.single1[back_dir])
end
return true
end
end
function tp.bend_rail(originpos, conn)
local pos=advtrains.dirCoordSet(originpos, conn)
local newdir=advtrains.oppd(conn)
local node=minetest.get_node(pos)
end
local function track_place_node(pos, node, ndef)
advtrains.ndb.swap_node(pos, node)
local ndef = minetest.registered_nodes[node.name]
local nnpref = ndef and ndef.at_nnpref
if not nnpref then return false end
local tr=tp.tracks[nnpref]
if not tr then return false end
--is rail already connected? no need to bend.
local conns=advtrains.get_track_connections(node.name, node.param2)
if advtrains.conn_matches_to(conn, conns) then
return
if ndef and ndef.after_place_node then
ndef.after_place_node(pos)
end
--rail at other end?
local adj1, adj2=tp.find_already_connected(pos)
if adj1 and adj2 then
return false--dont destroy existing track
elseif adj1 and not adj2 then
if tr.double_conn[adj1.."_"..newdir] then
advtrains.ndb.swap_node(pos, tr.double_conn[adj1.."_"..newdir])
return true--if exists, connect new rail and old end
end
return false
else
if tr.single_conn[newdir] then--just rotate old rail to right orientation
advtrains.ndb.swap_node(pos, tr.single_conn[newdir])
-- Main API function to place a track. Replaces the older "placetrack"
-- This function will attempt to place a track of the specified track placing group at the specified position, connecting it
-- with neighboring rails. Neighboring rails can themselves be replaced ("bent") within their own track place group,
-- if the player is permitted to do this.
-- Order of preference is:
-- Connect two track ends if possible
-- Connect one track end if any rail is near
-- Place the default track if no tracks are near
-- The function returns true on success.
function tp.place_track(pos, tpg, pname, yaw)
-- 1. collect neighboring tracks and whether they can be connected
local cand = {}
for i=0,15 do
if check_or_bend_rail(pos, i, pname) then
cand[#cand+1] = i
end
end
-- obtain the group table
local g = tp.groups[tpg]
-- 2. try all possible two-endpoint connections
for k1, conn1 in ipairs(cand) do
for k2, conn2 in ipairs(cand) do
if k1~=k2 then
-- order of conn1/conn2: prefer conn2 being in the direction of the player facing.
-- the combination the other way round will be run through in a later loop iteration
if advtrains.yawToDirection(yaw, conn1, conn2) == conn2 then
-- does there exist a suitable double-connection rail?
local node = g.double[conn1.."_"..conn2]
if node then
check_or_bend_rail(pos, conn1, pname, true)
check_or_bend_rail(pos, conn2, pname, true)
track_place_node(pos, node) -- calls after_place_node implicitly
return true
end
return false
end
end
function tp.placetrack(pos, nnpref, placer, itemstack, pointed_thing, yaw)
--1. find all rails that are likely to be connected
local tr=tp.tracks[nnpref]
local p_rails={}
local p_railpos={}
for i=0,15 do
if tp.rail_and_can_be_bent(pos, i, nnpref) then
p_rails[#p_rails+1]=i
p_railpos[i] = pos
end
end
-- 3. try all possible one_endpoint connections
for k1, conn1 in ipairs(cand) do
-- select single1 or single2? depending on yaw
local single
if advtrains.yawToDirection(yaw, conn1, advtrains.oppd(conn1)) == conn1 then
single = g.single1
else
local upos = {x=pos.x, y=pos.y-1, z=pos.z}
if tp.rail_and_can_be_bent(upos, i, nnpref) then
p_rails[#p_rails+1]=i
p_railpos[i] = upos
single = g.single2
end
local node = single[conn1]
if node then
check_or_bend_rail(pos, conn1, pname, true)
track_place_node(pos, node) -- calls after_place_node implicitly
return true
end
end
-- 4. if nothing worked, set the default
local node = g.default
track_place_node(pos, node) -- calls after_place_node implicitly
return true
end
-- try double_conn
if #p_rails > 1 then
--iterate subsets
for k1, conn1 in ipairs(p_rails) do
for k2, conn2 in ipairs(p_rails) do
if k1~=k2 then
local dconn1 = tr.double_conn_1
local dconn2 = tr.double_conn_2
if not (advtrains.yawToDirection(yaw, conn1, conn2) == conn1) then
dconn1 = tr.double_conn_2
dconn2 = tr.double_conn_1
end
-- Checks are made this way round so that dconn1 has priority (this will make arrows of atc rails
-- point in the right direction)
local using
if (dconn2[conn1.."_"..conn2]) then
using = dconn2[conn1.."_"..conn2]
end
if (dconn1[conn1.."_"..conn2]) then
using = dconn1[conn1.."_"..conn2]
end
if using then
-- has found a fitting rail in either direction
-- if not, continue loop
tp.bend_rail(p_railpos[conn1], conn1, nnpref)
tp.bend_rail(p_railpos[conn2], conn2, nnpref)
advtrains.ndb.swap_node(pos, using)
local nname=using.name
if minetest.registered_nodes[nname] and minetest.registered_nodes[nname].after_place_node then
minetest.registered_nodes[nname].after_place_node(pos, placer, itemstack, pointed_thing)
end
return
end
end
end
end
end
-- try single_conn
if #p_rails > 0 then
for ix, p_rail in ipairs(p_rails) do
local sconn1 = tr.single_conn_1
local sconn2 = tr.single_conn_2
if not (advtrains.yawToDirection(yaw, p_rail, (p_rail+8)%16) == p_rail) then
sconn1 = tr.single_conn_2
sconn2 = tr.single_conn_1
end
if sconn1[p_rail] then
local using = sconn1[p_rail]
tp.bend_rail(p_railpos[p_rail], p_rail, nnpref)
advtrains.ndb.swap_node(pos, using)
local nname=using.name
if minetest.registered_nodes[nname] and minetest.registered_nodes[nname].after_place_node then
minetest.registered_nodes[nname].after_place_node(pos, placer, itemstack, pointed_thing)
end
return
end
if sconn2[p_rail] then
local using = sconn2[p_rail]
tp.bend_rail(p_railpos[p_rail], p_rail, nnpref)
advtrains.ndb.swap_node(pos, using)
local nname=using.name
if minetest.registered_nodes[nname] and minetest.registered_nodes[nname].after_place_node then
minetest.registered_nodes[nname].after_place_node(pos, placer, itemstack, pointed_thing)
end
return
end
end
end
--use default
minetest.set_node(pos, {name=nnpref.."_"..tr.default})
if minetest.registered_nodes[nnpref.."_"..tr.default] and minetest.registered_nodes[nnpref.."_"..tr.default].after_place_node then
minetest.registered_nodes[nnpref.."_"..tr.default].after_place_node(pos, placer, itemstack, pointed_thing)
end
end
function tp.register_track_placer(nnprefix, imgprefix, dispname, def)
minetest.register_craftitem(":"..nnprefix.."_placer",{
description = dispname,
inventory_image = imgprefix.."_placer.png",
wield_image = imgprefix.."_placer.png",
groups={advtrains_trackplacer=1, digtron_on_place=1},
liquids_pointable = def.liquids_pointable,
on_place = function(itemstack, placer, pointed_thing)
local name = placer:get_player_name()
if not name then
return itemstack, false
end
if pointed_thing.type=="node" then
local pos=pointed_thing.above
local upos=vector.subtract(pointed_thing.above, {x=0, y=1, z=0})
if not advtrains.check_track_protection(pos, name) then
return itemstack, false
end
if minetest.registered_nodes[minetest.get_node(pos).name] and minetest.registered_nodes[minetest.get_node(pos).name].buildable_to then
local s
if def.suitable_substrate then
s = def.suitable_substrate(upos)
else
s = minetest.registered_nodes[minetest.get_node(upos).name] and minetest.registered_nodes[minetest.get_node(upos).name].walkable
end
if s then
-- minetest.chat_send_all(nnprefix)
local yaw = placer:get_look_horizontal()
tp.placetrack(pos, nnprefix, placer, itemstack, pointed_thing, yaw)
if not advtrains.is_creative(name) then
itemstack:take_item()
end
end
end
end
return itemstack, true
end,
})
end
-- TRACK WORKER --
minetest.register_craftitem("advtrains:trackworker",{
description = attrans("Track Worker Tool\n\nLeft-click: change rail type (straight/curve/switch)\nRight-click: rotate rail/bumper/signal/etc."),
@ -328,22 +221,17 @@ minetest.register_craftitem("advtrains:trackworker",{
end
local node=minetest.get_node(pos)
--if not advtrains.is_track_and_drives_on(minetest.get_node(pos).name, advtrains.all_tracktypes) then return end
-- New since 2.5: only the fields in the node definition are considered, no more hacky pattern matching on the nodename
local nnprefix, suffix, rotation=string.match(node.name, "^(.+)_([^_]+)(_[^_]+)$")
--atdebug(node.name.."\npattern recognizes:"..nnprefix.." / "..suffix.." / "..rotation)
--atdebug("nntab: ",tp.tracks[nnprefix])
if not tp.tracks[nnprefix] or not tp.tracks[nnprefix].twrotate[suffix] then
nnprefix, suffix=string.match(node.name, "^(.+)_([^_]+)$")
rotation = ""
if not tp.tracks[nnprefix] or not tp.tracks[nnprefix].twrotate[suffix] then
local ndef = minetest.registered_nodes[node.name]
if not ndef.advtrains or not ndef.advtrains.trackworker_next_rot then
minetest.chat_send_player(placer:get_player_name(), attrans("This node can't be rotated using the trackworker!"))
return
end
end
-- check if the node is modify-protected
if advtrains.is_track_and_drives_on(minetest.get_node(pos).name, advtrains.all_tracktypes) then
if advtrains.is_track(node.name) then
-- is a track, we can query
local can_modify, reason = advtrains.can_dig_or_modify_track(pos)
if not can_modify then
@ -363,22 +251,11 @@ minetest.register_craftitem("advtrains:trackworker",{
return
end
local modext=tp.tracks[nnprefix].twrotate[suffix]
if rotation==modext[#modext] then --increase param2
advtrains.ndb.swap_node(pos, {name=nnprefix.."_"..suffix..modext[1], param2=(node.param2+1)%4})
return
else
local modpos
for k,v in pairs(modext) do
if v==rotation then modpos=k end
end
if not modpos then
minetest.chat_send_player(placer:get_player_name(), attrans("This node can't be rotated using the trackworker!"))
return
end
advtrains.ndb.swap_node(pos, {name=nnprefix.."_"..suffix..modext[modpos+1], param2=node.param2})
local new_node = {name = ndef.advtrains.trackworker_next_rot, param2 = node.param2}
if ndef.advtrains.trackworker_rot_incr_param2 then
new_node.param2 = ((node.param2 + 1) % 4)
end
advtrains.ndb.swap_node(pos, new_node)
end
end,
on_use=function(itemstack, user, pointed_thing)
@ -393,38 +270,31 @@ minetest.register_craftitem("advtrains:trackworker",{
return
end
--if not advtrains.is_track_and_drives_on(minetest.get_node(pos).name, advtrains.all_tracktypes) then return end
if advtrains.get_train_at_pos(pos) then return end
local nnprefix, suffix, rotation=string.match(node.name, "^(.+)_([^_]+)(_[^_]+)$")
--atdebug(node.name.."\npattern recognizes:"..nodeprefix.." / "..railtype.." / "..rotation)
if not tp.tracks[nnprefix] or not tp.tracks[nnprefix].twcycle[suffix] then
nnprefix, suffix=string.match(node.name, "^(.+)_([^_]+)$")
rotation = ""
if not tp.tracks[nnprefix] or not tp.tracks[nnprefix].twcycle[suffix] then
minetest.chat_send_player(user:get_player_name(), attrans("This node can't be changed using the trackworker!"))
-- New since 2.5: only the fields in the node definition are considered, no more hacky pattern matching on the nodename
local ndef = minetest.registered_nodes[node.name]
if not ndef.advtrains or not ndef.advtrains.trackworker_next_var then
minetest.chat_send_player(placer:get_player_name(), attrans("This node can't be changed using the trackworker!"))
return
end
end
-- check if the node is modify-protected
if advtrains.is_track_and_drives_on(minetest.get_node(pos).name, advtrains.all_tracktypes) then
if advtrains.is_track(node.name) then
-- is a track, we can query
local can_modify, reason = advtrains.can_dig_or_modify_track(pos)
if not can_modify then
local str = attrans("This track can not be changed!")
local str = attrans("This track can not be rotated!")
if reason then
str = str .. " " .. reason
end
minetest.chat_send_player(user:get_player_name(), str)
minetest.chat_send_player(placer:get_player_name(), str)
return
end
end
local nextsuffix=tp.tracks[nnprefix].twcycle[suffix]
advtrains.ndb.swap_node(pos, {name=nnprefix.."_"..nextsuffix..rotation, param2=node.param2})
else
atprint(name, dump(tp.tracks))
local new_node = {name = ndef.advtrains.trackworker_next_var, param2 = node.param2}
advtrains.ndb.swap_node(pos, new_node)
end
end,
})

View File

@ -1,449 +1,128 @@
--advtrains by orwell96, see readme.txt
-- tracks.lua
-- rewritten with advtrains 2.5 according to new track registration system
--dev-time settings:
--EDIT HERE
--If the old non-model rails on straight tracks should be replaced by the new...
--false: no
--true: yes
advtrains.register_replacement_lbms=false
--[[TracksDefinition
nodename_prefix
texture_prefix
description
common={}
straight={}
straight45={}
curve={}
curve45={}
lswitchst={}
lswitchst45={}
rswitchst={}
rswitchst45={}
lswitchcr={}
lswitchcr45={}
rswitchcr={}
rswitchcr45={}
vert1={
--you'll probably want to override mesh here
--[[
Tracks in advtrains are defined by the node definition. They must have at least 2 connections, but can have any number.
Switchable nodes (turnouts, single/double-slip switches) are implemented by having a separate node (node name) for each of the possible states.
minetest.register_node(nodename, {
... usual node definition ...
groups = {
advtrains_track = 1,
advtrains_track_<tracktype>=1
^- these groups tell that the node is a track
not_blocking_trains=1,
^- this group tells that the node should not block trains although it's walkable.
},
at_rail_y = 0,
^- Height of this rail node (the y position of a wagon that stands centered on this rail)
at_conns = {
[1] = { c=0..15, y=0..1 },
[2] = { c=0..15, y=0..1 },
( [3] = { c=0..15, y=0..1 }, )
( [4] = { c=0..15, y=0..1 }, )
( ... )
}
vert2={
--you'll probably want to override mesh here
^- Connections of this rail. There are two general cases:
a) SIMPLE TRACK - the track has exactly 2 connections, and does not feature a turnout, crossing or other contraption
For simple tracks, except for the at_conns table no further setup needs to be specified. A train entering on conn 1 will go out at conn 2 and vice versa.
A track with only one connection defined is not permitted.
b) COMPOUND TRACK - the track has more than 2 connections
This will be used for turnouts and crossings. Tracks with more than 2 conns MUST define 'at_conn_map'.
Switchable nodes, whose state can be changed (e.g. turnouts) MUST define a 'state_map' within the advtrains table as well.
This differs from the behavior up until 2.4.2, where the conn mapping was fixed.
^- Connection definition:
- c is the direction of the connection (0-16). For the mapping to world directions see helpers.lua.
- Connections will be auto-rotated with param2 of the node (horizontal, param2 values 0-3 only)
- y is the height of the connection (rail will only connect when this matches)
^- The index of a connection inside the conns table (1, 2, 3, ...) is referred throughout advtrains code as 'connid'
^- IMPORTANT: For switchable nodes (any kind of turnout), it is crucial that for all of the node's variants the at_conns table stays the same. See below.
at_conn_map = {
[1] = 2,
[2] = 1,
[3] = 1,
}
^- Connection map of this rail. It specifies when a train enters the track on connid X, on which connid it will leave
This field MUST be specified when the number of connections in at_conns is greater than 2
This field may, and obviously will, vary between nodes for switchable nodes.
can_dig = advtrains.track_can_dig_callback
after_dig_node = advtrains.track_update_callback
after_place_node = advtrains.track_update_callback
^- the code in these 3 default minetest API functions is required for advtrains to work, however you can add your own code
on_rightclick = advtrains.state_node_on_rightclick_callback
^- Must be added if the node is a turnout and if it should be switched by right-click. It will cause the turnout to be switched to next_state.
advtrains = {
on_train_enter=function(pos, train_id, train, index) end
^- called when a train enters the rail
on_train_leave=function(pos, train_id, train, index) end
^- called when a train leaves the rail
-- The following function is only in effect when interlocking is enabled:
on_train_approach = function(pos, train_id, train, index, has_entered, lzbdata)
^- called when a train is approaching this position, called exactly once for every path recalculation (which can happen at any time)
^- This is called so that if the train would start braking now, it would come to halt about(wide approx) 5 nodes before the rail.
^- has_entered: when true, the train is already standing on this node with its front tip, and the enter callback has already been called.
Possibly, some actions need not to be taken in this case. Only set if it's the very first node the train is standing on.
^- lzbdata should be ignored and nothing should be assigned to it
-- The following information is required if the node is a turnout (e.g. can be switched into different positions)
node_state = "st"
^- The name of the state this node represents
^- Conventions for this field are as follows:
- Two-way straight/turn switches: 'st'=straight branch, 'cr'=diverting/turn branch
- 3-way turnouts, Y-turnouts: 'l'=left branch, 's'=straight branch, 'r'=right branch
node_next_state = "cr"
^- The name of the state that the turnout should be switched to when it is right-clicked
node_fallback_state = "st"
^- The name of the state that the turnout should "fall back" to when it is released
Only used by the interlocking system, when a route on the node is released it is switched back to this state.
node_state_map = {
["st"] = "<node name of the st variant>",
["cr"] = "<node name of the cr variant>",
... etc ...
}
^- Map of state name to the appropriate node name that should be set by advtrains when a switch is requested
Note that for all of those nodes, the at_conns table must be identical (however the conn_map will vary)
node_on_switch_state = function(pos, node, oldstate, newstate)
^- Called when the node state is switched by advtrains, after the node replacement has commenced.
Turnout switching can happen programmatically via advtrains.setstate(pos, state), via user right_click or via the interlocking system.
In no other situation is it permissible to exchange track nodes in-place, unless both at_conns and at_conn_map stay identical.
Note that the fields node_state, node_next_state and node_state_map completely replace the getstate/setstate functions.
There must be a one-to-one mapping between states and node names and no function can be defined for state switching.
This principle enables the seamless working of the interlocking autorouter and reduces failure points.
The node_state_* system can also be used as drop-in replacement for the passive-API-enabled nodes (andrews-cross, mesecon_switch etc.)
The advtrains API functions advtrains.getstate() and advtrains.setstate() remain the programmatic access points, but will now utilize the new state system.
trackworker_next_rot = <nodename of next rotation step>,
^- if set, right-click with trackworker will set this node
trackworker_rot_incr_param2 = true
^- if set, trackworker will increase node param2 on rightclick
trackworker_next_var = <nodename of next variant>
^- if set, left-click with trackworker will set this node
}
})
]]--
advtrains.all_tracktypes={}
--definition preparation
local function conns(c1, c2, r1, r2) return {{c=c1, y=r1}, {c=c2, y=r2}} end
local function conns3(c1, c2, c3, r1, r2, r3) return {{c=c1, y=r1}, {c=c2, y=r2}, {c=c3, y=r3}} end
-- This file provides some utilities to register tracks, but tries to not get into the way too much
advtrains.ap={}
advtrains.ap.t_30deg_flat={
regstep=1,
variant={
st={
conns = conns(0,8),
desc = "straight",
tpdouble = true,
tpsingle = true,
trackworker = "cr",
},
cr={
conns = conns(0,7),
desc = "curve",
tpdouble = true,
trackworker = "swlst",
},
swlst={
conns = conns3(0,8,7),
desc = "left switch (straight)",
trackworker = "swrst",
switchalt = "cr",
switchmc = "on",
switchst = "st",
switchprefix = "swl",
},
swlcr={
conns = conns3(0,7,8),
desc = "left switch (curve)",
trackworker = "swrcr",
switchalt = "st",
switchmc = "off",
switchst = "cr",
switchprefix = "swl",
},
swrst={
conns = conns3(0,8,9),
desc = "right switch (straight)",
trackworker = "st",
switchalt = "cr",
switchmc = "on",
switchst = "st",
switchprefix = "swr",
},
swrcr={
conns = conns3(0,9,8),
desc = "right switch (curve)",
trackworker = "st",
switchalt = "st",
switchmc = "off",
switchst = "cr",
switchprefix = "swr",
},
},
regtp=true,
tpdefault="st",
trackworker={
["swrcr"]="st",
["swrst"]="st",
["cr"]="swlst",
["swlcr"]="swrcr",
["swlst"]="swrst",
},
rotation={"", "_30", "_45", "_60"},
}
advtrains.ap.t_yturnout={
regstep=1,
variant={
l={
conns = conns3(0,7,9),
desc = "Y-turnout (left)",
switchalt = "r",
switchmc = "off",
switchst = "l",
switchprefix = "",
},
r={
conns = conns3(0,9,7),
desc = "Y-turnout (right)",
switchalt = "l",
switchmc = "on",
switchst = "r",
switchprefix = "",
}
},
regtp=true,
tpdefault="l",
rotation={"", "_30", "_45", "_60"},
}
advtrains.ap.t_s3way={
regstep=1,
variant={
l={
conns = { {c=0}, {c=7}, {c=8}, {c=9}, {c=0} },
desc = "3-way turnout (left)",
switchalt = "s",
switchst="l",
switchprefix = "",
},
s={
conns = { {c=0}, {c=8}, {c=7}, {c=9}, {c=0} },
desc = "3-way turnout (straight)",
switchalt ="r",
switchst = "s",
switchprefix = "",
},
r={
conns = { {c=0}, {c=9}, {c=8}, {c=7}, {c=0} },
desc = "3-way turnout (right)",
switchalt = "l",
switchst="r",
switchprefix = "",
}
},
regtp=true,
tpdefault="l",
rotation={"", "_30", "_45", "_60"},
}
advtrains.ap.t_30deg_slope={
regstep=1,
variant={
vst1={conns = conns(8,0,0,0.5), rail_y = 0.25, desc = "steep uphill 1/2", slope=true},
vst2={conns = conns(8,0,0.5,1), rail_y = 0.75, desc = "steep uphill 2/2", slope=true},
vst31={conns = conns(8,0,0,0.33), rail_y = 0.16, desc = "uphill 1/3", slope=true},
vst32={conns = conns(8,0,0.33,0.66), rail_y = 0.5, desc = "uphill 2/3", slope=true},
vst33={conns = conns(8,0,0.66,1), rail_y = 0.83, desc = "uphill 3/3", slope=true},
},
regsp=true,
slopeplacer={
[2]={"vst1", "vst2"},
[3]={"vst31", "vst32", "vst33"},
max=3,--highest entry
},
slopeplacer_45={
[2]={"vst1_45", "vst2_45"},
max=2,
},
rotation={"", "_30", "_45", "_60"},
trackworker={},
increativeinv={},
}
advtrains.ap.t_30deg_straightonly={
regstep=1,
variant={
st={
conns = conns(0,8),
desc = "straight",
tpdouble = true,
tpsingle = true,
trackworker = "st",
},
},
regtp=true,
tpdefault="st",
rotation={"", "_30", "_45", "_60"},
}
advtrains.ap.t_30deg_straightonly_noplacer={
regstep=1,
variant={
st={
conns = conns(0,8),
desc = "straight",
tpdouble = true,
tpsingle = true,
trackworker = "st",
},
},
tpdefault="st",
rotation={"", "_30", "_45", "_60"},
}
advtrains.ap.t_45deg={
regstep=2,
variant={
st={
conns = conns(0,8),
desc = "straight",
tpdouble = true,
tpsingle = true,
trackworker = "cr",
},
cr={
conns = conns(0,6),
desc = "curve",
tpdouble = true,
trackworker = "swlst",
},
swlst={
conns = conns3(0,8,6),
desc = "left switch (straight)",
trackworker = "swrst",
switchalt = "cr",
switchmc = "on",
switchst = "st",
},
swlcr={
conns = conns3(0,6,8),
desc = "left switch (curve)",
trackworker = "swrcr",
switchalt = "st",
switchmc = "off",
switchst = "cr",
},
swrst={
conns = conns3(0,8,10),
desc = "right switch (straight)",
trackworker = "st",
switchalt = "cr",
switchmc = "on",
switchst = "st",
},
swrcr={
conns = conns3(0,10,8),
desc = "right switch (curve)",
trackworker = "st",
switchalt = "st",
switchmc = "off",
switchst = "cr",
},
},
regtp=true,
tpdefault="st",
trackworker={
["swrcr"]="st",
["swrst"]="st",
["cr"]="swlst",
["swlcr"]="swrcr",
["swlst"]="swrst",
},
rotation={"", "_30", "_45", "_60"},
}
advtrains.ap.t_perpcrossing={
regstep = 1,
variant={
st={
conns = { {c=0}, {c=8}, {c=4}, {c=12} },
desc = "perpendicular crossing",
tpdouble = true,
tpsingle = true,
trackworker = "st",
},
},
regtp=true,
tpdefault="st",
rotation={"", "_30", "_45", "_60"},
}
advtrains.ap.t_90plusx_crossing={
regstep = 1,
variant={
["30l"]={
conns = { {c=0}, {c=8}, {c=1}, {c=9} },
desc = "30/90 degree crossing (left)",
tpdouble = true,
tpsingle = true,
trackworker = "45l"
},
["45l"]={
conns = { {c=0}, {c=8}, {c=2}, {c=10} },
desc = "45/90 degree crossing (left)",
tpdouble = true,
tpsingle = true,
trackworker = "60l",
},
["60l"]={
conns = { {c=0}, {c=8}, {c=3}, {c=11}},
desc = "60/90 degree crossing (left)",
tpdouble = true,
tpsingle = true,
trackworker = "60r",
},
["60r"]={
conns = { {c=0}, {c=8}, {c=5}, {c=13} },
desc = "60/90 degree crossing (right)",
tpdouble = true,
tpsingle = true,
trackworker = "45r"
},
["45r"]={
conns = { {c=0}, {c=8}, {c=6}, {c=14} },
desc = "45/90 degree crossing (right)",
tpdouble = true,
tpsingle = true,
trackworker = "30r",
},
["30r"]={
conns = { {c=0}, {c=8}, {c=7}, {c=15}},
desc = "30/90 degree crossing (right)",
tpdouble = true,
tpsingle = true,
trackworker = "30l",
},
},
regtp=true,
tpdefault="30l",
rotation={""},
trackworker = {
["30l"] = "45l",
["45l"] = "60l",
["60l"] = "60r",
["60r"] = "45r",
["45r"] = "30r",
["30r"] = "30l",
}
}
advtrains.ap.t_diagonalcrossing = {
regstep=1,
variant={
["30l45r"]={
conns = {{c=1}, {c=9}, {c=6}, {c=14}},
desc = "30left-45right diagonal crossing",
tpdouble=true,
tpsingle=true,
trackworker="60l30l",
},
["60l30l"]={
conns = {{c=3}, {c=11}, {c=1}, {c=9}},
desc = "30left-60right diagonal crossing",
tpdouble=true,
tpsingle=true,
trackworker="60l45r"
},
["60l45r"]={
conns = {{c=3}, {c=11}, {c=6}, {c=14}},
desc = "60left-45right diagonal crossing",
tpdouble=true,
tpsingle=true,
trackworker="60l60r"
},
["60l60r"]={
conns = {{c=3}, {c=11}, {c=5}, {c=13}},
desc = "60left-60right diagonal crossing",
tpdouble=true,
tpsingle=true,
trackworker="60r45l",
},
--If 60l60r had a mirror image, it would be here, but it's symmetric.
-- 60l60r is also equivalent to 30l30r but rotated 90 degrees.
["60r45l"]={
conns = {{c=5}, {c=13}, {c=2}, {c=10}},
desc = "60right-45left diagonal crossing",
tpdouble=true,
tpsingle=true,
trackworker="60r30r",
},
["60r30r"]={
conns = {{c=5}, {c=13}, {c=7}, {c=15}},
desc = "60right-30right diagonal crossing",
tpdouble=true,
tpsingle=true,
trackworker="30r45l",
},
["30r45l"]={
conns = {{c=7}, {c=15}, {c=2}, {c=10}},
desc = "30right-45left diagonal crossing",
tpdouble=true,
tpsingle=true,
trackworker="30l45r",
},
},
regtp=true,
tpdefault="30l45r",
rotation={""},
trackworker = {
["30l45r"] = "60l30l",
["60l30l"] = "60l45r",
["60l45r"] = "60l60r",
["60l60r"] = "60r45l",
["60r45l"] = "60r30r",
["60r30r"] = "30r45l",
["30r45l"] = "30l45r",
}
}
advtrains.trackpresets = advtrains.ap
--definition format: ([] optional)
--[[{
nodename_prefix
texture_prefix
[shared_texture]
models_prefix
models_suffix (with dot)
[shared_model]
formats={
st,cr,swlst,swlcr,swrst,swrcr,vst1,vst2
(each a table with indices 0-3, for if to register a rail with this 'rotation' table entry. nil is assumed as 'all', set {} to not register at all)
}
common={} change something on common rail appearance
}
[18.12.17] Note on new connection system:
In order to support real rail crossing nodes and finally make the trackplacer respect switches, I changed the connection system.
There can be a variable number of connections available. These are specified as tuples {c=<connection>, y=<rely>}
The table "at_conns" consists of {<conn1>, <conn2>...}
the "at_rail_y" property holds the value that was previously called "railheight"
Depending on the number of connections:
2 conns: regular rail
3 conns: switch:
- when train passes in at conn1, will move out of conn2
- when train passes in at conn2 or conn3, will move out of conn1
4 conns: cross (or cross switch, depending on arrangement of conns):
- conn1 <> conn2
- conn3 <> conn4
]]
-- Notify the user if digging the rail is not allowed
local function can_dig_callback(pos, player)
function advtrains.track_can_dig_callback(pos, player)
local ok, reason = advtrains.can_dig_or_modify_track(pos)
if not ok and player then
minetest.chat_send_player(player:get_player_name(), attrans("This track can not be removed!") .. " " .. reason)
@ -451,144 +130,122 @@ local function can_dig_callback(pos, player)
return ok
end
function advtrains.register_tracks(tracktype, def, preset)
advtrains.trackplacer.register_tracktype(def.nodename_prefix, preset.tpdefault)
if preset.regtp then
advtrains.trackplacer.register_track_placer(def.nodename_prefix, def.texture_prefix, def.description, def)
end
if preset.regsp then
advtrains.slope.register_placer(def, preset)
end
for suffix, var in pairs(preset.variant) do
for rotid, rotation in ipairs(preset.rotation) do
if not def.formats[suffix] or def.formats[suffix][rotid] then
local img_suffix = suffix..rotation
local ndef = advtrains.merge_tables({
description=def.description.."("..(var.desc or "any")..rotation..")",
drawtype = "mesh",
paramtype="light",
paramtype2="facedir",
walkable = false,
selection_box = {
type = "fixed",
fixed = {-1/2-1/16, -1/2, -1/2, 1/2+1/16, -1/2+2/16, 1/2},
},
mesh = def.shared_model or (def.models_prefix.."_"..img_suffix..def.models_suffix),
tiles = {def.shared_texture or (def.texture_prefix.."_"..img_suffix..".png"), def.second_texture},
groups = {
attached_node = advtrains.IGNORE_WORLD and 0 or 1,
advtrains_track=1,
["advtrains_track_"..tracktype]=1,
save_in_at_nodedb=1,
dig_immediate=2,
not_in_creative_inventory=1,
not_blocking_trains=1,
},
can_dig = can_dig_callback,
after_dig_node=function(pos)
function advtrains.track_update_callback(pos)
advtrains.ndb.update(pos)
end,
after_place_node=function(pos)
advtrains.ndb.update(pos)
end,
at_nnpref = def.nodename_prefix,
at_suffix = suffix,
at_rotation = rotation,
at_rail_y = var.rail_y
}, def.common or {})
if preset.regtp then
ndef.drop = def.nodename_prefix.."_placer"
end
if preset.regsp and var.slope then
ndef.drop = def.nodename_prefix.."_slopeplacer"
end
--connections
ndef.at_conns = advtrains.rotate_conn_by(var.conns, (rotid-1)*preset.regstep)
local ndef_avt_table
if var.switchalt and var.switchst then
local switchfunc=function(pos, node, newstate)
newstate = newstate or var.switchalt -- support for 3 (or more) state switches
-- this code is only called from the internal setstate function, which
-- ensures that it is safe to switch the turnout
if newstate~=var.switchst then
advtrains.ndb.swap_node(pos, {name=def.nodename_prefix.."_"..(var.switchprefix or "")..newstate..rotation, param2=node.param2})
advtrains.invalidate_all_paths(pos)
end
end
ndef.on_rightclick = function(pos, node, player)
function advtrains.state_node_on_rightclick_callback(pos, node, player)
if advtrains.check_turnout_signal_protection(pos, player:get_player_name()) then
advtrains.setstate(pos, nil, node)
local ndef = minetest.registered_nodes[node.name]
if ndef and ndef.advtrains and ndef.advtrains.node_next_state then
advtrains.setstate(pos, ndef.advtrains.node_next_state, node)
advtrains.log("Switch", player:get_player_name(), pos)
end
end
if var.switchmc then
ndef.mesecons = {effector = {
["action_"..var.switchmc] = function(pos, node)
advtrains.setstate(pos, nil, node)
end,
rules=advtrains.meseconrules
}}
end
ndef_avt_table = {
getstate = var.switchst,
setstate = switchfunc,
-- advtrains.register_node_4rot(name, nodedef)
-- Registers four rotations for the node defined by nodedef (0°, 30°, 45° and 60°; the 4 90°-steps are already handled by the param2, resulting in 16 directions total).
-- You must provide the definition for the base node, and certain fields are altered automatically for the 3 additional rotations:
-- name: appends the suffix "_30", "_45" or "_60"
-- description: appends the rotation (human-readable) in parenthesis
-- tiles_prefix: if defined, "tiles" field will be set as prefix..rotationExtension..".png"
-- mesh_prefix, mesh_suffix: if defined, "mesh" field will be set as prefix..rotationExtension..suffix
-- at_conns: are rotated according to the node rotation
-- node_state_map, trackworker_next_var: appends the suffix appropriately.
-- groups: applies save_in_at_nodedb and not_blocking_trains groups if not already present
-- The nodes are registered in the trackworker to be rotated with right-click.
-- definition_mangling_function is an optional parameter. For each of the 4 rotations, it gets passed the modified node definition and may perform final modifications to it.
-- signature: function definition_mangling_function(name, nodedef, rotationIndex, rotationSuffix)
-- Example usage: define the setstate function of turnouts (if that is not done via the "automatic" way of state_node_map)
local rotations = {
{i = 0, s = "", h = " (0)", n = "_30"},
{i = 1, s = "_30", h = " (30)", n = "_45"},
{i = 2, s = "_45", h = " (45)", n = "_60"},
{i = 3, s = "_60", h = " (60)", n = ""},
}
function advtrains.register_node_4rot(ori_name, ori_ndef, definition_mangling_function)
for _, rot in ipairs(rotations) do
local ndef = table.copy(ori_ndef)
if ori_ndef.advtrains then
-- make sure advtrains table is deep-copied because we may need to replace node_state_map
ndef.advtrains = table.copy(ori_ndef.advtrains)
else
ndef.advtrains = {} -- we need the table later for trackworker
end
-- Perform the name mangling
local suffix = rot.s
local name = ori_name..suffix
ndef.description = ori_ndef.description .. rot.h
if ori_ndef.tiles_prefix then
ndef.tiles = { ori_ndef.tiles_prefix .. suffix .. ".png" }
end
if ori_ndef.mesh_prefix then
ndef.mesh = ori_ndef.mesh_prefix .. suffix .. ori_ndef.mesh_suffix
end
-- rotate connections
if ori_ndef.at_conns then
ndef.at_conns = advtrains.rotate_conn_by(ori_ndef.at_conns, rot.i)
end
-- update node state map if present
if ori_ndef.advtrains then
if ori_ndef.advtrains.node_state_map then
local new_nsm = {}
for state, nname in pairs(ori_ndef.advtrains.node_state_map) do
new_nsm[state] = nname .. suffix
end
ndef.advtrains.node_state_map = new_nsm
end
if ori_ndef.advtrains.trackworker_next_var then
ndef.advtrains.trackworker_next_var = ori_ndef.advtrains.trackworker_next_var .. suffix
end
-- apply trackworker rot field
ndef.advtrains.trackworker_next_rot = ori_name .. rot.n
ndef.advtrains.trackworker_rot_incr_param2 = (rot.n=="")
end
-- apply groups
ndef.groups.save_in_at_nodedb = 1
ndef.groups.not_blocking_trains = 1
-- give the definition mangling function an option to do some adjustments
if definition_mangling_function then
definition_mangling_function(name, ndef, rot.i, suffix)
end
local adef={}
if def.get_additional_definiton then
adef=def.get_additional_definiton(def, preset, suffix, rotation)
end
ndef = advtrains.merge_tables(ndef, adef)
-- register node
atdebug("Registering: ",name, ndef)
minetest.register_node(":"..name, ndef)
-- insert getstate/setstate functions after merging the additional definitions
if ndef_avt_table then
ndef.advtrains = advtrains.merge_tables(ndef.advtrains or {}, ndef_avt_table)
-- if this has the track_place_group set, register as a candidate for the track_place_group
if ndef.advtrains.track_place_group then
advtrains.trackplacer.register_candidate(ndef.advtrains.track_place_group, name, ndef, ndef.advtrains.track_place_single, true)
end
end
end
minetest.register_node(":"..def.nodename_prefix.."_"..suffix..rotation, ndef)
--trackplacer
if preset.regtp then
local tpconns = {conn1=ndef.at_conns[1].c, conn2=ndef.at_conns[2].c}
if var.tpdouble then
advtrains.trackplacer.add_double_conn(def.nodename_prefix, suffix, rotation, tpconns)
end
if var.tpsingle then
advtrains.trackplacer.add_single_conn(def.nodename_prefix, suffix, rotation, tpconns)
end
end
advtrains.trackplacer.add_worked(def.nodename_prefix, suffix, rotation, var.trackworker)
end
end
end
advtrains.all_tracktypes[tracktype]=true
-- Registers an item to place and automatically connect nearby tracks
function advtrains.register_track_placer(...)
end
function advtrains.is_track_and_drives_on(nodename, drives_on_p)
local drives_on = drives_on_p
if not drives_on then drives_on = advtrains.all_tracktypes end
local hasentry = false
for _,_ in pairs(drives_on) do
hasentry=true
end
if not hasentry then drives_on = advtrains.all_tracktypes end
-- Registers an item to place and adjust slope tracks
function advtrains.register_slope_placer(...)
end
-- track-related helper functions
function advtrains.is_track(nodename)
if not minetest.registered_nodes[nodename] then
return false
end
local nodedef=minetest.registered_nodes[nodename]
for k,v in pairs(drives_on) do
if nodedef.groups["advtrains_track_"..k] then
if nodedef and nodedef.groups.advtrains_track then
return true
end
end
return false
end
@ -628,124 +285,3 @@ function advtrains.can_dig_or_modify_track(pos)
end
return true
end
-- slope placer. Defined in register_tracks.
--crafted with rail and gravel
local sl={}
function sl.register_placer(def, preset)
minetest.register_craftitem(":"..def.nodename_prefix.."_slopeplacer",{
description = attrans("@1 Slope", def.description),
inventory_image = def.texture_prefix.."_slopeplacer.png",
wield_image = def.texture_prefix.."_slopeplacer.png",
groups={},
on_place = sl.create_slopeplacer_on_place(def, preset)
})
end
--(itemstack, placer, pointed_thing)
function sl.create_slopeplacer_on_place(def, preset)
return function(istack, player, pt)
if not pt.type=="node" then
minetest.chat_send_player(player:get_player_name(), attrans("Can't place: not pointing at node"))
return istack
end
local pos=pt.above
if not pos then
minetest.chat_send_player(player:get_player_name(), attrans("Can't place: not pointing at node"))
return istack
end
local node=minetest.get_node(pos)
if not minetest.registered_nodes[node.name] or not minetest.registered_nodes[node.name].buildable_to then
minetest.chat_send_player(player:get_player_name(), attrans("Can't place: space occupied!"))
return istack
end
if not advtrains.check_track_protection(pos, player:get_player_name()) then
minetest.record_protection_violation(pos, player:get_player_name())
return istack
end
--determine player orientation (only horizontal component)
--get_look_horizontal may not be available
local yaw=player.get_look_horizontal and player:get_look_horizontal() or (player:get_look_yaw() - math.pi/2)
--rounding unit vectors is a nice way for selecting 1 of 8 directions since sin(30°) is 0.5.
local dirvec={x=math.floor(math.sin(-yaw)+0.5), y=0, z=math.floor(math.cos(-yaw)+0.5)}
--translate to direction to look up inside the preset table
local param2, rot45=({
[-1]={
[-1]=2,
[0]=3,
[1]=3,
},
[0]={
[-1]=2,
[1]=0,
},
[1]={
[-1]=1,
[0]=1,
[1]=0,
},
})[dirvec.x][dirvec.z], dirvec.x~=0 and dirvec.z~=0
local lookup=preset.slopeplacer
if rot45 then lookup=preset.slopeplacer_45 end
--go unitvector forward and look how far the next node is
local step=1
while step<=lookup.max do
local node=minetest.get_node(vector.add(pos, dirvec))
--next node solid?
if not minetest.registered_nodes[node.name] or not minetest.registered_nodes[node.name].buildable_to or advtrains.is_protected(pos, player:get_player_name()) then
--do slopes of this distance exist?
if lookup[step] then
if minetest.settings:get_bool("creative_mode") or istack:get_count()>=step then
--start placing
local placenodes=lookup[step]
while step>0 do
minetest.set_node(pos, {name=def.nodename_prefix.."_"..placenodes[step], param2=param2})
if not minetest.settings:get_bool("creative_mode") then
istack:take_item()
end
step=step-1
pos=vector.subtract(pos, dirvec)
end
else
minetest.chat_send_player(player:get_player_name(), attrans("Can't place: Not enough slope items left (@1 required)", step))
end
else
minetest.chat_send_player(player:get_player_name(), attrans("Can't place: There's no slope of length @1",step))
end
return istack
end
step=step+1
pos=vector.add(pos, dirvec)
end
minetest.chat_send_player(player:get_player_name(), attrans("Can't place: no supporting node at upper end."))
return itemstack
end
end
advtrains.slope=sl
--END code, BEGIN definition
--definition format: ([] optional)
--[[{
nodename_prefix
texture_prefix
[shared_texture]
models_prefix
models_suffix (with dot)
[shared_model]
formats={
st,cr,swlst,swlcr,swrst,swrcr,vst1,vst2
(each a table with indices 0-3, for if to register a rail with this 'rotation' table entry. nil is assumed as 'all', set {} to not register at all)
}
common={} change something on common rail appearance
}]]

View File

@ -283,7 +283,7 @@ function advtrains.train_ensure_init(id, train)
assertdef(train, "id", id)
if not train.drives_on or not train.max_speed then
if not train.max_speed then
--atprint("in ensure_init: missing properties, updating!")
advtrains.update_trainpart_properties(id)
end
@ -1034,10 +1034,9 @@ end
-- Note: safe_decouple_wagon() has been moved to wagons.lua
-- this function sets wagon's pos_in_train(parts) properties and train's max_speed and drives_on (and more)
-- this function sets wagon's pos_in_train(parts) properties and train's max_speed (and more)
function advtrains.update_trainpart_properties(train_id, invert_flipstate)
local train=advtrains.trains[train_id]
train.drives_on=advtrains.merge_tables(advtrains.all_tracktypes)
--FIX: deep-copy the table!!!
train.max_speed=20
train.extent_h = 0;
@ -1079,13 +1078,6 @@ function advtrains.update_trainpart_properties(train_id, invert_flipstate)
end
rel_pos=rel_pos+wagon.wagon_span
if wagon.drives_on then
for k,_ in pairs(train.drives_on) do
if not wagon.drives_on[k] then
train.drives_on[k]=nil
end
end
end
train.max_speed=math.min(train.max_speed, wagon.max_speed)
train.extent_h = math.max(train.extent_h, wagon.extent_h or 1);
end

View File

@ -1367,7 +1367,7 @@ function advtrains.register_wagon(sysname_p, prototype, desc, inv_img, nincreati
local node=minetest.get_node_or_nil(pointed_thing.under)
if not node then atprint("[advtrains]Ignore at placer position") return itemstack end
local nodename=node.name
if(not advtrains.is_track_and_drives_on(nodename, prototype.drives_on)) then
if(not advtrains.is_track(nodename)) then
atprint("no track here, not placing.")
return itemstack
end
@ -1382,7 +1382,7 @@ function advtrains.register_wagon(sysname_p, prototype, desc, inv_img, nincreati
local yaw = placer:get_look_horizontal()
local plconnid = advtrains.yawToClosestConn(yaw, tconns)
local prevpos = advtrains.get_adjacent_rail(pointed_thing.under, tconns, plconnid, prototype.drives_on)
local prevpos = advtrains.get_adjacent_rail(pointed_thing.under, tconns, plconnid)
if not prevpos then
minetest.chat_send_player(pname, "The track you are trying to place the wagon on is not long enough!")
return
@ -1407,7 +1407,6 @@ advtrains.register_wagon("advtrains:wagon_placeholder", {
collisionbox = {-0.3,-0.3,-0.3, 0.3,0.3,0.3},
visual_size = {x=0.7, y=0.7},
initial_sprite_basepos = {x=0, y=0},
drives_on = advtrains.all_tracktypes,
max_speed = 5,
seats = {
},

View File

@ -24,7 +24,9 @@ dofile(modpath.."tool.lua")
dofile(modpath.."approach.lua")
dofile(modpath.."ars.lua")
dofile(modpath.."tsr_rail.lua")
--TODO reenable tsr rail
--dofile(modpath.."tsr_rail.lua")
minetest.register_privilege("interlocking", {description = "Can set up track sections, routes and signals.", give_to_singleplayer = true})

View File

@ -20,7 +20,9 @@ local modpath = minetest.get_modpath(minetest.get_current_modname()) .. DIR_DELI
dofile(modpath.."railwaytime.lua")
dofile(modpath.."scheduler.lua")
dofile(modpath.."stoprail.lua")
--TODO reenable stop rail
--dofile(modpath.."stoprail.lua")
function advtrains.lines.load(data)

View File

@ -113,15 +113,6 @@ local suppasp_ra = {
}
}
advtrains.trackplacer.register_tracktype("advtrains_signals_ks:hs")
advtrains.trackplacer.register_tracktype("advtrains_signals_ks:ra")
advtrains.trackplacer.register_tracktype("advtrains_signals_ks:sign")
advtrains.trackplacer.register_tracktype("advtrains_signals_ks:sign_lf")
advtrains.trackplacer.register_tracktype("advtrains_signals_ks:sign_lf7")
advtrains.trackplacer.register_tracktype("advtrains_signals_ks:zs3")
advtrains.trackplacer.register_tracktype("advtrains_signals_ks:zs3v")
advtrains.trackplacer.register_tracktype("advtrains_signals_ks:mast")
for _, rtab in ipairs({
{rot = "0", sbox = {-1/8, -1/2, -1/2, 1/8, 1/2, -1/4}, ici=true},
{rot = "30", sbox = {-3/8, -1/2, -1/2, -1/8, 1/2, -1/4},},
@ -201,7 +192,7 @@ for _, rtab in ipairs({
after_dig_node = advtrains.interlocking.signal_after_dig,
})
-- rotatable by trackworker
advtrains.trackplacer.add_worked("advtrains_signals_ks:hs", typ, "_"..rot)
--TODO add rotation using trackworker
end
@ -246,7 +237,7 @@ for _, rtab in ipairs({
after_dig_node = advtrains.interlocking.signal_after_dig,
})
-- rotatable by trackworker
advtrains.trackplacer.add_worked("advtrains_signals_ks:ra", typ, "_"..rot)
--TODO add rotation using trackworker
end
-- Schilder:
@ -283,7 +274,7 @@ for _, rtab in ipairs({
after_dig_node = advtrains.interlocking.signal_after_dig,
})
-- rotatable by trackworker
advtrains.trackplacer.add_worked("advtrains_signals_ks:"..prefix, typ, "_"..rot, nxt)
--TODO add rotation using trackworker
end
for typ, prts in pairs {
@ -378,7 +369,7 @@ for _, rtab in ipairs({
t.drop = "advtrains_signals_ks:zs3_off_0"
t.selection_box.fixed[1][5] = 0
minetest.register_node("advtrains_signals_ks:zs3_"..typ.."_"..rot, t)
advtrains.trackplacer.add_worked("advtrains_signals_ks:zs3", typ, "_"..rot)
--TODO add rotation using trackworker
-- Zs 3v
local t = table.copy(def)
@ -387,7 +378,7 @@ for _, rtab in ipairs({
t.drop = "advtrains_signals_ks:zs3v_off_0"
t.tiles[3] = t.tiles[3] .. "^[multiply:yellow"
minetest.register_node("advtrains_signals_ks:zs3v_"..typ.."_"..rot, t)
advtrains.trackplacer.add_worked("advtrains_signals_ks:zs3v", typ, "_"..rot)
--TODO add rotation using trackworker
end
minetest.register_node("advtrains_signals_ks:mast_mast_"..rot, {
@ -412,7 +403,7 @@ for _, rtab in ipairs({
},
drop = "advtrains_signals_ks:mast_mast_0",
})
advtrains.trackplacer.add_worked("advtrains_signals_ks:mast","mast", "_"..rot)
--TODO add rotation using trackworker
end
-- Crafting

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,937 @@
-- Default tracks for advtrains
-- (c) orwell96 and contributors
local default_boxen = {
["st"] = {
[""] = {
selection_box = {
type = "fixed",
fixed = {-1/2-1/16, -1/2, -1/2, 1/2+1/16, -1/2+2/16, 1/2},
}
},
["_30"] = {
selection_box = {
type = "fixed",
fixed = {
{-0.5000, -0.5000, -1.000, 0.5000, -0.3750, 1.000},
{-0.8750, -0.5000, -1.000, -0.5000, -0.3750, 0.2500},
{0.5000, -0.5000, -0.2500, 0.8750, -0.3750, 1.000},
{-0.1250, -0.5000, -1.375, 0.1875, -0.3750, -1.000}
}
}
},
["_45"] = {
selection_box = {
type = "fixed",
fixed = {
{-0.5000, -0.5000, -0.8750, 0.5000, -0.3750, 0.8750},
{0.5000, -0.5000, -0.5000, 0.8750, -0.3750, 0.5000},
{-0.8750, -0.5000, -0.5000, -0.5000, -0.3750, 0.5000}
}
}
},
["_60"] = {
selection_box = {
type = "fixed",
fixed = {
{-1.000, -0.5000, -0.5000, 1.000, -0.3750, 0.5000},
{-1.000, -0.5000, -0.8750, 0.2500, -0.3750, -0.5000},
{-0.2500, -0.5000, 0.5000, 1.000, -0.3750, 0.8750},
{-1.375, -0.5000, -0.1250, -1.000, -0.3750, 0.1875}
}
}
},
},
["cr"] = {
[""] = {
selection_box = {
type = "fixed",
fixed = {
{-0.5000, -0.5000, -0.5000, 0.6875, -0.3750, 0.5000},
{-0.3750, -0.5000, -1.000, 1.000, -0.3750, 0.000}
}
}
},
["_30"] = {
selection_box = {
type = "fixed",
fixed = {
{-0.5000, -0.5000, -0.5000, 0.7500, -0.3750, 0.8750},
{-0.3750, -0.5000, 0.8750, 0.2500, -0.3750, 1.188},
{0.7500, -0.5000, 0.2500, 1.063, -0.3750, 0.8750}
}
}
},
["_45"] = {
selection_box = {
type = "fixed",
fixed = {
{-0.5000, -0.5000, -1.125, 0.5000, -0.3750, 0.6875},
{-0.8750, -0.5000, -0.9375, -0.5000, -0.3750, 0.06250},
{0.5000, -0.5000, -0.5000, 0.8750, -0.3750, 0.5000}
}
}
},
["_60"] = {
selection_box = {
type = "fixed",
fixed = {
{-0.8125, -0.5000, -0.5000, 1.188, -0.3750, 0.5000},
{-0.1875, -0.5000, 0.5000, 0.8750, -0.3125, 0.8750},
{-0.2500, -0.5000, -0.9375, 0.3125, -0.3125, -0.5000}
}
}
},
},
["swlst"] = {
[""] = {
selection_box = {
type = "fixed",
fixed = {
{-0.5000, -0.5000, -0.5000, 0.6250, -0.3750, 0.5000},
{-0.3125, -0.5000, -1.000, 0.9375, -0.3125, -0.06250}
}
}
},
["_30"] = {
selection_box = {
type = "fixed",
fixed = {
{-0.5000, -0.5000, -1.000, 0.5000, -0.3750, 1.000},
{-0.8750, -0.5000, -1.000, -0.5000, -0.3750, 0.2500},
{0.5000, -0.5000, -0.2500, 0.8750, -0.3750, 1.000},
{-0.1250, -0.5000, -1.375, 0.1875, -0.3750, -1.000}
}
}
},
["_45"] = {
selection_box = {
type = "fixed",
fixed = {
{-0.5000, -0.5000, -1.1875, 0.5000, -0.3750, 0.8750},
{0.5000, -0.5000, -0.5000, 0.8750, -0.3750, 0.5000},
{-0.8750, -0.5000, -0.8125, -0.5000, -0.3750, 0.5000}
}
}
},
["_60"] = {
selection_box = {
type = "fixed",
fixed = {
{-1.000, -0.5000, -0.5000, 1.000, -0.3750, 0.5000},
{-1.000, -0.5000, -0.8750, 0.2500, -0.3750, -0.5000},
{-0.2500, -0.5000, 0.5000, 1.000, -0.3750, 0.8750},
{-1.375, -0.5000, -0.1250, -1.000, -0.3750, 0.1875}
}
}
},
},
["swrst"] = {
[""] = {
selection_box = {
type = "fixed",
fixed = {
{-0.5000, -0.5000, -0.5000, 0.6250, -0.3750, 0.5000},
{-0.8125, -0.5000, -1.000, 0.4375, -0.3125, -0.06250}
}
}
},
["_30"] = {
selection_box = {
type = "fixed",
fixed = {
{-0.5000, -0.5000, -1.000, 0.5000, -0.3750, 1.000},
{-0.8750, -0.5000, -1.000, -0.5000, -0.3750, 0.2500},
{0.5000, -0.5000, -0.2500, 0.8750, -0.3750, 1.000},
{-0.1250, -0.5000, -1.375, 0.1875, -0.3750, -1.000}
}
}
},
["_45"] = {
selection_box = {
type = "fixed",
fixed = {
{-1.1875, -0.5000, -0.5000, 0.8750, -0.3750, 0.5000},
{-0.5000, -0.5000, 0.5000, 0.5000, -0.3750, 0.8750},
{-0.8125, -0.5000, -0.8750, 0.5000, -0.3750, -0.5000}
}
}
},
["_60"] = {
selection_box = {
type = "fixed",
fixed = {
{-1.000, -0.5000, -0.5000, 1.000, -0.3750, 0.5000},
{-1.000, -0.5000, -0.8750, 0.2500, -0.3750, -0.5000},
{-0.2500, -0.5000, 0.5000, 1.000, -0.3750, 0.8750},
{-1.375, -0.5000, -0.1250, -1.000, -0.3750, 0.1875}
}
}
},
},
}
default_boxen["swlcr"] = default_boxen["swlst"]
default_boxen["swrcr"] = default_boxen["swrst"]
--flat
advtrains.register_tracks("default", {
nodename_prefix="advtrains:dtrack",
texture_prefix="advtrains_dtrack",
models_prefix="advtrains_dtrack",
models_suffix=".b3d",
shared_texture="advtrains_dtrack_shared.png",
description=attrans("Track"),
formats={},
get_additional_definiton = function(def, preset, suffix, rotation)
if default_boxen[suffix] ~= nil and default_boxen[suffix][rotation] ~= nil then
return default_boxen[suffix][rotation]
else
return {}
end
end,
}, advtrains.ap.t_30deg_flat)
minetest.register_craft({
output = 'advtrains:dtrack_placer 50',
recipe = {
{'default:steel_ingot', 'group:stick', 'default:steel_ingot'},
{'default:steel_ingot', 'group:stick', 'default:steel_ingot'},
{'default:steel_ingot', 'group:stick', 'default:steel_ingot'},
},
})
local y3_boxen = {
[""] = {
selection_box = {
type = "fixed",
fixed = {
{-0.8750, -0.5000, -1.125, 0.8750, -0.3750, 0.4375}
}
}
},
["_30"] = {
selection_box = {
type = "fixed",
fixed = {
{-0.5000, -0.5000, -0.875, 0.5000, -0.3750, 1.000},
{-0.8750, -0.5000, -0.4375, -0.5000, -0.3750, 0.5625},
{0.5000, -0.5000, -0.2500, 0.8125, -0.3750, 1.000},
}
}
},
--UX FIXME: - 3way - have to place straight route before l and r or the
--nodebox overlaps too much and can't place the straight track node.
["_45"] = {
selection_box = {
type = "fixed",
fixed = {
{-0.5000, -0.5000, -1.1250, 0.5000, -0.3750, 0.8750},
{0.5000, -0.5000, -0.5000, 0.8750, -0.3750, 0.5000},
{-1.1250, -0.5000, -0.9375, -0.5000, -0.3750, 0.5000}
}
}
},
["_60"] = {
selection_box = {
type = "fixed",
fixed = {
--{-0.5000, -0.5000, -0.875, 0.5000, -0.3750, 1.000},
{-0.875, -0.5000, -0.5, 1.0, -0.3750, 0.5},
--{-0.8750, -0.5000, -0.4375, -0.5000, -0.3750, 0.5625},
{-0.4375, -0.5000, -0.8750, 0.5625, -0.3750, -0.5000},
--{0.5000, -0.5000, -0.2500, 0.8125, -0.3750, 1.000},
{-0.2500, -0.5000, -0.2500, 1.0000, -0.3750, 0.8125},
}
}
},
}
local function y3_turnouts_addef(def, preset, suffix, rotation)
return y3_boxen[rotation] or {}
end
-- y-turnout
advtrains.register_tracks("default", {
nodename_prefix="advtrains:dtrack_sy",
texture_prefix="advtrains_dtrack_sy",
models_prefix="advtrains_dtrack_sy",
models_suffix=".obj",
shared_texture="advtrains_dtrack_shared.png",
description=attrans("Y-turnout"),
formats = {},
get_additional_definiton = y3_turnouts_addef,
}, advtrains.ap.t_yturnout)
minetest.register_craft({
output = 'advtrains:dtrack_sy_placer 2',
recipe = {
{'advtrains:dtrack_placer', '', 'advtrains:dtrack_placer'},
{'', 'advtrains:dtrack_placer', ''},
{'', 'advtrains:dtrack_placer', ''},
},
})
--3-way turnout
advtrains.register_tracks("default", {
nodename_prefix="advtrains:dtrack_s3",
texture_prefix="advtrains_dtrack_s3",
models_prefix="advtrains_dtrack_s3",
models_suffix=".obj",
shared_texture="advtrains_dtrack_shared.png",
description=attrans("3-way turnout"),
formats = {},
get_additional_definiton = y3_turnouts_addef,
}, advtrains.ap.t_s3way)
minetest.register_craft({
output = 'advtrains:dtrack_s3_placer 1',
recipe = {
{'advtrains:dtrack_placer', 'advtrains:dtrack_placer', 'advtrains:dtrack_placer'},
{'', 'advtrains:dtrack_placer', ''},
{'', '', ''},
},
})
-- Diamond Crossings
local perp_boxen = {
[""] = {}, --default size
["_30"] = {
selection_box = {
type = "fixed",
fixed = {
{-1.000, -0.5000, -1.000, 1.000, -0.3750, 1.000}
}
}
},
["_45"] = {
selection_box = {
type = "fixed",
fixed = {
{-0.8125, -0.5000, -0.8125, 0.8125, -0.3750, 0.8125}
}
}
},
["_60"] = {
selection_box = {
type = "fixed",
fixed = {
{-1.000, -0.5000, -1.000, 1.000, -0.3750, 1.000}
}
}
},
}
-- perpendicular
advtrains.register_tracks("default", {
nodename_prefix="advtrains:dtrack_xing",
texture_prefix="advtrains_dtrack_xing",
models_prefix="advtrains_dtrack_xing",
models_suffix=".obj",
shared_texture="advtrains_dtrack_shared.png",
description=attrans("Perpendicular Diamond Crossing Track"),
formats = {},
get_additional_definiton = function(def, preset, suffix, rotation)
return perp_boxen[rotation] or {}
end
}, advtrains.ap.t_perpcrossing)
minetest.register_craft({
output = 'advtrains:dtrack_xing_placer 3',
recipe = {
{'', 'advtrains:dtrack_placer', ''},
{'advtrains:dtrack_placer', 'advtrains:dtrack_placer', 'advtrains:dtrack_placer'},
{'', 'advtrains:dtrack_placer', ''}
}
})
local ninety_plus_boxen = {
["30l"] = {
selection_box = {
type = "fixed",
fixed = {
{-0.5000, -0.5000, -1.000, 0.5000, -0.3750, 1.000},
{-0.8750, -0.5000, -1.000, -0.5000, -0.3750, 0.2500},
{0.5000, -0.5000, -0.2500, 0.8750, -0.3750, 1.000},
{-0.1250, -0.5000, -1.375, 0.1875, -0.3750, -1.000}
}
}
},
["30r"] = {
selection_box = {
type = "fixed",
fixed = {
{0.5000, -0.5000, -1.000, -0.5000, -0.3750, 1.000},
{0.8750, -0.5000, -1.000, 0.5000, -0.3750, 0.2500},
{-0.5000, -0.5000, -0.2500, -0.8750, -0.3750, 1.000},
{0.1250, -0.5000, -1.375, -0.1875, -0.3750, -1.000}
}
}
},
["45l"] = {
selection_box = {
type = "fixed",
fixed = {
{-0.5000, -0.5000, -0.8750, 0.5000, -0.3750, 0.8750},
{0.5000, -0.5000, -0.5000, 0.8750, -0.3750, 0.5000},
{-0.8750, -0.5000, -0.5000, -0.5000, -0.3750, 0.5000}
}
}
},
["45r"] = {
selection_box = {
type = "fixed",
fixed = {
{-0.5000, -0.5000, -0.8750, 0.5000, -0.3750, 0.8750},
{0.5000, -0.5000, -0.5000, 0.8750, -0.3750, 0.5000},
{-0.8750, -0.5000, -0.5000, -0.5000, -0.3750, 0.5000}
}
}
},
["60l"] = {
selection_box = {
type = "fixed",
fixed = {
{-1.000, -0.5000, -0.5000, 1.000, -0.3750, 0.5000},
{-1.000, -0.5000, -0.8750, 0.2500, -0.3750, -0.5000},
{-0.2500, -0.5000, 0.5000, 1.000, -0.3750, 0.8750},
{-1.375, -0.5000, -0.1250, -1.000, -0.3750, 0.1875}
}
}
},
["60r"] = {
selection_box = {
type = "fixed",
fixed = {
{1.000, -0.5000, -0.5000, -1.000, -0.3750, 0.5000},
{1.000, -0.5000, -0.8750, -0.2500, -0.3750, -0.5000},
{0.2500, -0.5000, 0.5000, -1.000, -0.3750, 0.8750},
{1.375, -0.5000, -0.1250, 1.000, -0.3750, 0.1875}
}
}
},
}
-- 90plusx
-- When you face east and param2=0, then this set of rails has a rail at 90
-- degrees to the viewer, plus another rail crossing at 30, 45 or 60 degrees.
advtrains.register_tracks("default", {
nodename_prefix="advtrains:dtrack_xing90plusx",
texture_prefix="advtrains_dtrack_xing4590",
models_prefix="advtrains_dtrack_xing90plusx",
models_suffix=".obj",
shared_texture="advtrains_dtrack_shared.png",
description=attrans("90+Angle Diamond Crossing Track"),
formats = {},
get_additional_definiton = function(def, preset, suffix, rotation)
return ninety_plus_boxen[suffix] or {}
end,
}, advtrains.ap.t_90plusx_crossing)
minetest.register_craft({
output = 'advtrains:dtrack_xing90plusx_placer 2',
recipe = {
{'advtrains:dtrack_placer', '', ''},
{'advtrains:dtrack_placer', 'advtrains:dtrack_placer', 'advtrains:dtrack_placer'},
{'', '', 'advtrains:dtrack_placer'}
}
})
-- Deprecate any rails using the old name scheme
minetest.register_lbm({
label = "Upgrade legacy 4590 rails",
name = "advtrains_train_track:replace_legacy_4590",
nodenames = {"advtrains:dtrack_xing4590_st"},
run_at_every_load = true,
action = function(pos, node)
minetest.log("actionPos!: " .. pos.x .. "," .. pos.y .. "," .. pos.z)
minetest.log("node!: " .. node.name .. "," .. node.param1 .. "," .. node.param2)
advtrains.ndb.swap_node(pos,
{
name="advtrains:dtrack_xing90plusx_45l",
param1=node.param1,
param2=node.param2,
})
end
})
-- This will replace any items left in the inventory
minetest.register_alias("advtrains:dtrack_xing4590_placer", "advtrains:dtrack_xing90plusx_placer")
local diagonal_boxen = {
["30r45l"] = {
selection_box = {
type = "fixed",
fixed = {
{0.5000, -0.5000, -1.000, -0.5000, -0.3750, 1.000},
{0.8750, -0.5000, -1.000, 0.5000, -0.3750, 0.2500},
{-0.5000, -0.5000, -0.2500, -0.8750, -0.3750, 1.000},
{0.1250, -0.5000, -1.375, -0.1875, -0.3750, -1.000}
}
}
},
["60l30l"] = {
selection_box = {
type = "fixed",
fixed = {
{-1.000, -0.5000, -0.5000, 1.000, -0.3750, 0.5000},
{-1.000, -0.5000, -0.8750, 0.2500, -0.3750, -0.5000},
{-0.2500, -0.5000, 0.5000, 1.000, -0.3750, 0.8750},
{-1.375, -0.5000, -0.1250, -1.000, -0.3750, 0.1875}
}
}
},
["60l60r"] = {
selection_box = {
type = "fixed",
fixed = {
{-1.000, -0.5000, -1.000, 1.000, -0.3750, 1.000}
}
}
},
["60r30r"] = {
selection_box = {
type = "fixed",
fixed = {
{1.000, -0.5000, -0.5000, -1.000, -0.3750, 0.5000},
{1.000, -0.5000, -0.8750, -0.2500, -0.3750, -0.5000},
{0.2500, -0.5000, 0.5000, -1.000, -0.3750, 0.8750},
{1.375, -0.5000, -0.1250, 1.000, -0.3750, 0.1875}
}
}
},
["30l45r"] = {
selection_box = {
type = "fixed",
fixed = {
{-0.5000, -0.5000, -1.000, 0.5000, -0.3750, 1.000},
{-0.8750, -0.5000, -1.000, -0.5000, -0.3750, 0.2500},
{0.5000, -0.5000, -0.2500, 0.8750, -0.3750, 1.000},
{-0.1250, -0.5000, -1.375, 0.1875, -0.3750, -1.000}
}
}
},
["60l45r"] = {
selection_box = {
type = "fixed",
fixed = {
{-1.000, -0.5000, -0.5000, 1.000, -0.3750, 0.5000},
{-1.000, -0.5000, -0.8750, 0.2500, -0.3750, -0.5000},
{-0.2500, -0.5000, 0.5000, 1.000, -0.3750, 0.8750},
{-1.375, -0.5000, -0.1250, -1.000, -0.3750, 0.1875}
}
}
},
["60r45l"] = {
selection_box = {
type = "fixed",
fixed = {
{1.000, -0.5000, -0.5000, -1.000, -0.3750, 0.5000},
{1.000, -0.5000, -0.8750, -0.2500, -0.3750, -0.5000},
{0.2500, -0.5000, 0.5000, -1.000, -0.3750, 0.8750},
{1.375, -0.5000, -0.1250, 1.000, -0.3750, 0.1875}
}
}
},
}
-- Diagonal
-- This set of rail crossings is named based on the angle of each intersecting
-- direction when facing east and param2=0. Rails with l/r swapped are mirror
-- images. For example, 30r45l is the mirror image of 30l45r.
advtrains.register_tracks("default", {
nodename_prefix="advtrains:dtrack_xingdiag",
texture_prefix="advtrains_dtrack_xingdiag",
models_prefix="advtrains_dtrack_xingdiag",
models_suffix=".obj",
shared_texture="advtrains_dtrack_shared.png",
description=attrans("Diagonal Diamond Crossing Track"),
formats = {},
get_additional_definiton = function(def, preset, suffix, rotation)
return diagonal_boxen[suffix] or {}
end,
}, advtrains.ap.t_diagonalcrossing)
minetest.register_craft({
output = 'advtrains:dtrack_xingdiag_placer 2',
recipe = {
{'advtrains:dtrack_placer', '', 'advtrains:dtrack_placer'},
{'', 'advtrains:dtrack_placer', ''},
{'advtrains:dtrack_placer', '', 'advtrains:dtrack_placer'}
}
})
---- Not included: very shallow crossings like (30/60)+45.
---- At an angle of only 18.4 degrees, the models would not
---- translate well to a block game.
-- END crossings
--slopes
advtrains.register_tracks("default", {
nodename_prefix="advtrains:dtrack",
texture_prefix="advtrains_dtrack",
models_prefix="advtrains_dtrack",
models_suffix=".obj",
shared_texture="advtrains_dtrack_shared.png",
second_texture="default_gravel.png",
description=attrans("Track"),
formats={vst1={true, false, true}, vst2={true, false, true}, vst31={true}, vst32={true}, vst33={true}},
}, advtrains.ap.t_30deg_slope)
minetest.register_craft({
type = "shapeless",
output = 'advtrains:dtrack_slopeplacer 2',
recipe = {
"advtrains:dtrack_placer",
"advtrains:dtrack_placer",
"default:gravel",
},
})
--bumpers
advtrains.register_tracks("default", {
nodename_prefix="advtrains:dtrack_bumper",
texture_prefix="advtrains_dtrack_bumper",
models_prefix="advtrains_dtrack_bumper",
models_suffix=".b3d",
shared_texture="advtrains_dtrack_rail.png",
--bumpers still use the old texture until the models are redone.
description=attrans("Bumper"),
formats={},
}, advtrains.ap.t_30deg_straightonly)
minetest.register_craft({
output = 'advtrains:dtrack_bumper_placer 2',
recipe = {
{'group:wood', 'dye:red'},
{'default:steel_ingot', 'default:steel_ingot'},
{'advtrains:dtrack_placer', 'advtrains:dtrack_placer'},
},
})
--legacy bumpers
for _,rot in ipairs({"", "_30", "_45", "_60"}) do
minetest.register_alias("advtrains:dtrack_bumper"..rot, "advtrains:dtrack_bumper_st"..rot)
end
-- atc track
advtrains.register_tracks("default", {
nodename_prefix="advtrains:dtrack_atc",
texture_prefix="advtrains_dtrack_atc",
models_prefix="advtrains_dtrack",
models_suffix=".b3d",
shared_texture="advtrains_dtrack_shared_atc.png",
description=attrans("ATC controller"),
formats={},
get_additional_definiton = advtrains.atc_function
}, advtrains.trackpresets.t_30deg_straightonly)
-- Tracks for loading and unloading trains
-- Copyright (C) 2017 Gabriel Pérez-Cerezo <gabriel@gpcf.eu>
local function get_far_node(pos)
local node = minetest.get_node(pos)
if node.name == "ignore" then
minetest.get_voxel_manip():read_from_map(pos, pos)
node = minetest.get_node(pos)
end
return node
end
local function show_fc_formspec(pos,player)
local pname = player:get_player_name()
if minetest.is_protected(pos,pname) then
minetest.chat_send_player(pname, "Position is protected!")
return
end
local meta = minetest.get_meta(pos)
local fc = meta:get_string("fc") or ""
local form = 'formspec_version[4]'..
'size[10,5]'..
'label[0.5,0.4;Advtrains Loading/Unloading Track]'..
'label[0.5,1.1;Set the code to match against the wagon\'s freight code]'..
'label[0.5,1.6;A blank field matches all wagons (default)]'..
'label[0.5,2.1;Use code # to disable the track section]'..
'field[0.5,3;5.5,1;fc;FC;'..minetest.formspec_escape(fc)..']'..
'button[6.5,3;3,1;save;Submit]'
minetest.show_formspec(pname, "at_load_unload_"..advtrains.encode_pos(pos), form)
end
minetest.register_on_player_receive_fields(function(player, formname, fields)
local pname = player:get_player_name()
local pe = string.match(formname, "^at_load_unload_(............)$")
local pos = advtrains.decode_pos(pe)
if pos then
if minetest.is_protected(pos, pname) then
minetest.chat_send_player(pname, "Position is protected!")
return
end
if fields.save then
minetest.get_meta(pos):set_string("fc",tostring(fields.fc))
minetest.chat_send_player(pname,"Freight code set: "..tostring(fields.fc))
show_fc_formspec(pos,player)
end
end
end)
local function train_load(pos, train_id, unload)
local train=advtrains.trains[train_id]
local below = get_far_node({x=pos.x, y=pos.y-1, z=pos.z})
if not string.match(below.name, "chest") then
atprint("this is not a chest! at "..minetest.pos_to_string(pos))
return
end
local node_fc = minetest.get_meta(pos):get_string("fc") or ""
if node_fc == "#" then
--track section is disabled
return
end
local inv = minetest.get_inventory({type="node", pos={x=pos.x, y=pos.y-1, z=pos.z}})
if inv and train.velocity < 2 then
for k, v in ipairs(train.trainparts) do
local i=minetest.get_inventory({type="detached", name="advtrains_wgn_"..v})
if i and i:get_list("box") then
local wagon_data = advtrains.wagons[v]
local wagon_fc
if wagon_data.fc then
if not wagon_data.fcind then wagon_data.fcind = 1 end
wagon_fc = tostring(wagon_data.fc[wagon_data.fcind]) or ""
end
if node_fc == "" or wagon_fc == node_fc then
if not unload then
for _, item in ipairs(inv:get_list("main")) do
if i:get_list("box") and i:room_for_item("box", item) then
i:add_item("box", item)
inv:remove_item("main", item)
end
end
else
for _, item in ipairs(i:get_list("box")) do
if inv:get_list("main") and inv:room_for_item("main", item) then
i:remove_item("box", item)
inv:add_item("main", item)
end
end
end
end
end
end
end
end
advtrains.register_tracks("default", {
nodename_prefix="advtrains:dtrack_unload",
texture_prefix="advtrains_dtrack_unload",
models_prefix="advtrains_dtrack",
models_suffix=".b3d",
shared_texture="advtrains_dtrack_shared_unload.png",
description=attrans("Unloading Track"),
formats={},
get_additional_definiton = function(def, preset, suffix, rotation)
return {
after_dig_node=function(pos)
advtrains.invalidate_all_paths()
advtrains.ndb.clear(pos)
end,
on_rightclick = function(pos, node, player)
show_fc_formspec(pos, player)
end,
advtrains = {
on_train_enter = function(pos, train_id)
train_load(pos, train_id, true)
end,
},
}
end
}, advtrains.trackpresets.t_30deg_straightonly)
advtrains.register_tracks("default", {
nodename_prefix="advtrains:dtrack_load",
texture_prefix="advtrains_dtrack_load",
models_prefix="advtrains_dtrack",
models_suffix=".b3d",
shared_texture="advtrains_dtrack_shared_load.png",
description=attrans("Loading Track"),
formats={},
get_additional_definiton = function(def, preset, suffix, rotation)
return {
after_dig_node=function(pos)
advtrains.invalidate_all_paths()
advtrains.ndb.clear(pos)
end,
on_rightclick = function(pos, node, player)
show_fc_formspec(pos, player)
end,
advtrains = {
on_train_enter = function(pos, train_id)
train_load(pos, train_id, false)
end,
},
}
end
}, advtrains.trackpresets.t_30deg_straightonly)
-- mod-dependent crafts
local loader_core = "default:mese_crystal" --fallback
if minetest.get_modpath("basic_materials") then
loader_core = "basic_materials:ic"
elseif minetest.get_modpath("technic") then
loader_core = "technic:control_logic_unit"
end
--print("Loader Core: "..loader_core)
minetest.register_craft({
type="shapeless",
output = 'advtrains:dtrack_load_placer',
recipe = {
"advtrains:dtrack_placer",
loader_core,
"default:chest"
},
})
loader_core = nil --nil the crafting variable
--craft between load/unload tracks
minetest.register_craft({
type="shapeless",
output = 'advtrains:dtrack_unload_placer',
recipe = {
"advtrains:dtrack_load_placer",
},
})
minetest.register_craft({
type="shapeless",
output = 'advtrains:dtrack_load_placer',
recipe = {
"advtrains:dtrack_unload_placer",
},
})
if mesecon then
advtrains.register_tracks("default", {
nodename_prefix="advtrains:dtrack_detector_off",
texture_prefix="advtrains_dtrack_detector",
models_prefix="advtrains_dtrack",
models_suffix=".b3d",
shared_texture="advtrains_dtrack_shared_detector_off.png",
description=attrans("Detector Rail"),
formats={},
get_additional_definiton = function(def, preset, suffix, rotation)
return {
mesecons = {
receptor = {
state = mesecon.state.off,
rules = advtrains.meseconrules
}
},
advtrains = {
on_updated_from_nodedb = function(pos, node)
mesecon.receptor_off(pos, advtrains.meseconrules)
end,
on_train_enter=function(pos, train_id)
advtrains.ndb.swap_node(pos, {name="advtrains:dtrack_detector_on".."_"..suffix..rotation, param2=advtrains.ndb.get_node(pos).param2})
if advtrains.is_node_loaded(pos) then
mesecon.receptor_on(pos, advtrains.meseconrules)
end
end
}
}
end
}, advtrains.ap.t_30deg_straightonly)
advtrains.register_tracks("default", {
nodename_prefix="advtrains:dtrack_detector_on",
texture_prefix="advtrains_dtrack",
models_prefix="advtrains_dtrack",
models_suffix=".b3d",
shared_texture="advtrains_dtrack_shared_detector_on.png",
description="Detector(on)(you hacker you)",
formats={},
get_additional_definiton = function(def, preset, suffix, rotation)
return {
mesecons = {
receptor = {
state = mesecon.state.on,
rules = advtrains.meseconrules
}
},
advtrains = {
on_updated_from_nodedb = function(pos, node)
mesecon.receptor_on(pos, advtrains.meseconrules)
end,
on_train_leave=function(pos, train_id)
advtrains.ndb.swap_node(pos, {name="advtrains:dtrack_detector_off".."_"..suffix..rotation, param2=advtrains.ndb.get_node(pos).param2})
if advtrains.is_node_loaded(pos) then
mesecon.receptor_off(pos, advtrains.meseconrules)
end
end
}
}
end
}, advtrains.ap.t_30deg_straightonly_noplacer)
minetest.register_craft({
type="shapeless",
output = 'advtrains:dtrack_detector_off_placer',
recipe = {
"advtrains:dtrack_placer",
"mesecons:wire_00000000_off"
},
})
end
--TODO legacy
--I know lbms are better for this purpose
for name,rep in pairs({swl_st="swlst", swr_st="swrst", swl_cr="swlcr", swr_cr="swrcr", }) do
minetest.register_abm({
-- In the following two fields, also group:groupname will work.
nodenames = {"advtrains:track_"..name},
interval = 1.0, -- Operation interval in seconds
chance = 1, -- Chance of trigger per-node per-interval is 1.0 / this
action = function(pos, node, active_object_count, active_object_count_wider) minetest.set_node(pos, {name="advtrains:track_"..rep, param2=node.param2}) end,
})
minetest.register_abm({
-- In the following two fields, also group:groupname will work.
nodenames = {"advtrains:track_"..name.."_45"},
interval = 1.0, -- Operation interval in seconds
chance = 1, -- Chance of trigger per-node per-interval is 1.0 / this
action = function(pos, node, active_object_count, active_object_count_wider) minetest.set_node(pos, {name="advtrains:track_"..rep.."_45", param2=node.param2}) end,
})
end
if advtrains.register_replacement_lbms then
minetest.register_lbm({
name = "advtrains:ramp_replacement_1",
-- In the following two fields, also group:groupname will work.
nodenames = {"advtrains:track_vert1"},
action = function(pos, node, active_object_count, active_object_count_wider) minetest.set_node(pos, {name="advtrains:dtrack_vst1", param2=(node.param2+2)%4}) end,
})
minetest.register_lbm({
name = "advtrains:ramp_replacement_1",
-- -- In the following two fields, also group:groupname will work.
nodenames = {"advtrains:track_vert2"},
action = function(pos, node, active_object_count, active_object_count_wider) minetest.set_node(pos, {name="advtrains:dtrack_vst2", param2=(node.param2+2)%4}) end,
})
minetest.register_abm({
name = "advtrains:st_rep_1",
-- In the following two fields, also group:groupname will work.
nodenames = {"advtrains:track_st"},
interval=1,
chance=1,
action = function(pos, node, active_object_count, active_object_count_wider) minetest.set_node(pos, {name="advtrains:dtrack_st", param2=node.param2}) end,
})
minetest.register_lbm({
name = "advtrains:st_rep_1",
-- -- In the following two fields, also group:groupname will work.
nodenames = {"advtrains:track_st_45"},
action = function(pos, node, active_object_count, active_object_count_wider) minetest.set_node(pos, {name="advtrains:dtrack_st_45", param2=node.param2}) end,
})
end