--trackplacer.lua--holds code for the track-placing system. the default 'track' item will be a craftitem that places rails as needed. this will neither place or change switches nor place vertical rails.--all new trackplacer codelocal tp={
tracks={}}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={},}endfunction tp.add_double_conn(nnprefix, suffix, rotation, conns)local nodename=nnprefix.."_"..suffix..rotation
for i=0,3do
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}end
tp.tracks[nnprefix].modify[nodename]=trueendfunction tp.add_single_conn(nnprefix, suffix, rotation, conns)local nodename=nnprefix.."_"..suffix..rotation
for i=0,3do
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}end
tp.tracks[nnprefix].modify[nodename]=trueendfunction 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]={}endtable.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 functionistrackandbc(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)thenlocal 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)thenlocal cconns=advtrains.get_track_connections(cnode.name, cnode.param2)return advtrains.conn_matches_to(conn, cconns)endreturn falseendfunction 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 inipairs(dconns)doifistrackandbc(pos, conn)thenif found_conn then--we found one in previous iterationreturn true,true--signal that it's connectedelse
found_conn = conn.c
endendendreturn found_conn
endfunction tp.rail_and_can_be_bent(originpos, conn)local pos=advtrains.dirCoordSet(originpos, conn)local newdir=(conn+8)%16local node=minetest.get_node(pos)if not advtrains.is_track_and_drives_on(node.name, advtrains.all_tracktypes)thenreturn falseendlocal ndef=minetest.registered_nodes[node.name]local nnpref = ndef and ndef.at_nnpref
if not nnpref then return false endlocal tr=tp.tracks[nnpref]if not tr then return false endif 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)thenlocal cconns=advtrains.get_track_connections(node.name, node.param2)return advtrains.conn_matches_to(conn, cconns)endend-- If the rail is not allowed to be modified, also only use if already in desired directionif not advtrains.can_dig_or_modify_track(pos)thenlocal 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 thenreturn false--dont destroy existing trackelseif adj1 and not adj2 thenif tr.double_conn[adj1.."_"..newdir]thenreturn true--if exists, connect new rail and old endendreturn falseelseif tr.single_conn[newdir]then--just rotate old rail to right orientationreturn trueendreturn falseendendfunction tp.bend_rail(originpos, conn)local pos=advtrains.dirCoordSet(originpos, conn)local newdir=advtrains.oppd(conn)local node=minetest.get_node(pos)local ndef=minetest.registered_nodes[node.name]local nnpref = ndef and ndef.at_nnpref
if not nnpref then return false endlocal 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)thenreturnend--rail at other end?local adj1, adj2=tp.find_already_connected(pos)if adj1 and adj2 thenreturn false--dont destroy existing trackelseif adj1 and not adj2 thenif 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 endendreturn falseelseif tr.single_conn[newdir]then--just rotate old rail to right orientation
advtrains.ndb.swap_node(pos, tr.single_conn[newdir])return trueendreturn falseendendfunction tp.placetrack(pos, nnpref, placer, itemstack, pointed_thing, yaw)--1. find all rails that are likely to be connectedlocal tr=tp.tracks[nnpref]local p_rails={}local p_railpos={}for i=0,15doif tp.rail_and_can_be_bent(pos, i, nnpref)then
p_rails[#p_rails+1]=i
p_railpos[i] = pos
elselocal 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
endendend-- try double_connif#p_rails >1then--iterate subsetsfor k1, conn1 inipairs(p_rails)dofor k2, conn2 inipairs(p_rails)doif k1~=k2 thenlocal 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]endif(dconn1[conn1.."_"..conn2])then
using = dconn1[conn1.."_"..conn2]endif 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)endreturnendendendendend-- try single_connif#p_rails >0thenfor ix, p_rail inipairs(p_rails)dolocal 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
endif sconn1[p_rail]thenlocal 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)endreturnendif sconn2[p_rail]thenlocal 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)endreturnendendend--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)endendfunction 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 thenreturn itemstack,falseendif pointed_thing.type=="node"thenlocal 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)thenreturn itemstack,falseendif minetest.registered_nodes[minetest.get_node(pos).name]and minetest.registered_nodes[minetest.get_node(pos).name].buildable_to thenlocal 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
endif 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()endendendendreturn itemstack,trueend,})end
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."),
groups = {cracky=1},-- key=name, value=rating; rating=1..3.
inventory_image ="advtrains_trackworker.png",
wield_image ="advtrains_trackworker.png",
stack_max =1,
on_place =function(itemstack, placer, pointed_thing)local name = placer:get_player_name()if not name thenreturnendlocal has_aux1_down = placer:get_player_control().aux1
if pointed_thing.type=="node"thenlocal pos=pointed_thing.under
if not advtrains.check_track_protection(pos, name)thenreturnendlocal node=minetest.get_node(pos)--if not advtrains.is_track_and_drives_on(minetest.get_node(pos).name, advtrains.all_tracktypes) then return endlocal 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
minetest.chat_send_player(placer:get_player_name(),attrans("This node can't be rotated using the trackworker!"))returnendend-- check if the node is modify-protectedif advtrains.is_track_and_drives_on(minetest.get_node(pos).name, advtrains.all_tracktypes)then-- is a track, we can querylocal can_modify, reason = advtrains.can_dig_or_modify_track(pos)if not can_modify thenlocal str =attrans("This track can not be rotated!")if reason then
str = str .." ".. reason
end
minetest.chat_send_player(placer:get_player_name(), str)returnendendif has_aux1_down then--feature: flip the node by 180°--i've always wanted this!
advtrains.ndb.swap_node(pos, {name=node.name, param2=(node.param2+2)%4})returnendlocal 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})returnelselocal modpos
for k,v inpairs(modext)doif v==rotation then modpos=k endendif not modpos then
minetest.chat_send_player(placer:get_player_name(),attrans("This node can't be rotated using the trackworker!"))returnend
advtrains.ndb.swap_node(pos, {name=nnprefix.."_"..suffix..modext[modpos+1], param2=node.param2})endendend,
on_use=function(itemstack, user, pointed_thing)local name = user:get_player_name()if not name thenreturnendif pointed_thing.type=="node"thenlocal pos=pointed_thing.under
local node=minetest.get_node(pos)if not advtrains.check_track_protection(pos, name)thenreturnend--if not advtrains.is_track_and_drives_on(minetest.get_node(pos).name, advtrains.all_tracktypes) then return endif advtrains.get_train_at_pos(pos)then return endlocal 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!"))returnendend-- check if the node is modify-protectedif advtrains.is_track_and_drives_on(minetest.get_node(pos).name, advtrains.all_tracktypes)then-- is a track, we can querylocal can_modify, reason = advtrains.can_dig_or_modify_track(pos)if not can_modify thenlocal str =attrans("This track can not be changed!")if reason then
str = str .." ".. reason
end
minetest.chat_send_player(user:get_player_name(), str)returnendendlocal nextsuffix=tp.tracks[nnprefix].twcycle[suffix]
advtrains.ndb.swap_node(pos, {name=nnprefix.."_"..nextsuffix..rotation, param2=node.param2})elseatprint(name,dump(tp.tracks))endend,})--putting into right place
advtrains.trackplacer=tp