From 46c4447da089146c662f217bf3269d78d4c462c2 Mon Sep 17 00:00:00 2001 From: orwell96 Date: Mon, 18 Dec 2017 21:44:36 +0100 Subject: Rewrite rail connection system... ...to support an arbitrary number of connections for rails, which leads to these new features: - switches now get recognized by the trackworker correctly - ability to add real rail crosses During this, I also rewrote the rail registering system and the conway function (important part of path prediction) Note, developers: the track preset format changed, you might need to rewrite them according to the presets in tracks.lua if you wrote your own (possibly breaks advcarts) --- advtrains/atc.lua | 8 +- advtrains/helpers.lua | 197 ++++++++++--------- advtrains/init.lua | 12 +- advtrains/nodedb.lua | 26 +-- advtrains/path.lua | 82 ++++++++ advtrains/signals.lua | 28 +-- advtrains/trackplacer.lua | 53 +++-- advtrains/tracks.lua | 480 ++++++++++++++++++++-------------------------- advtrains/trainlogic.lua | 57 ------ advtrains/wagons.lua | 9 +- 10 files changed, 474 insertions(+), 478 deletions(-) create mode 100644 advtrains/path.lua (limited to 'advtrains') diff --git a/advtrains/atc.lua b/advtrains/atc.lua index c8f7c90..bdcf144 100644 --- a/advtrains/atc.lua +++ b/advtrains/atc.lua @@ -124,8 +124,8 @@ advtrains.atc_function = function(def, preset, suffix, rotation) meta:set_string("formspec", atc.get_atc_controller_formspec(pos, meta)) local pts=minetest.pos_to_string(pos) - local _, conn1=advtrains.get_rail_info_at(pos, advtrains.all_tracktypes) - atc.controllers[pts]={command=fields.command, arrowconn=conn1} + local _, conns=advtrains.get_rail_info_at(pos, advtrains.all_tracktypes) + atc.controllers[pts]={command=fields.command, arrowconn=conns[1].c} if advtrains.detector.on_node[pts] then atc.send_command(pos) end @@ -145,8 +145,8 @@ function atc.get_atc_controller_formspec(pos, meta) local command=meta:get_string("command") local command_on=meta:get_string("command_on") local channel=meta:get_string("channel") - local formspec="size[8,6]".. - "dropdown[0,0;3;mode;static,mesecon,digiline;"..mode.."]" + local formspec="size[8,6]" + -- "dropdown[0,0;3;mode;static,mesecon,digiline;"..mode.."]" if mode<3 then formspec=formspec.."field[0.5,1.5;7,1;command;"..attrans("Command")..";"..minetest.formspec_escape(command).."]" if tonumber(mode)==2 then diff --git a/advtrains/helpers.lua b/advtrains/helpers.lua index f2e969f..3864d81 100644 --- a/advtrains/helpers.lua +++ b/advtrains/helpers.lua @@ -52,97 +52,6 @@ function atround(number) return math.floor(number+0.5) end ---vertical_transmit: ---[[ -rely1, rely2 tell to which height the connections are pointed to. 1 means it will go up the next node - -]] - -function advtrains.conway(midreal, prev, drives_on)--in order prev,mid,return - local mid=advtrains.round_vector_floor_y(midreal) - - local midnode_ok, middir1, middir2, midrely1, midrely2=advtrains.get_rail_info_at(mid, drives_on) - if not midnode_ok then - return nil - end - - local next, chkdir, chkrely, y_offset - y_offset=0 - --atprint(" in order mid1,mid2",middir1,middir2) - --try if it is dir1 - local cor1=advtrains.dirCoordSet(mid, middir2)--<<<< - if cor1.x==prev.x and cor1.z==prev.z then--this was previous - next=advtrains.dirCoordSet(mid, middir1) - if midrely1>=1 then - next.y=next.y+1 - --atprint("found midrely1 to be >=1: next is now "..(next and minetest.pos_to_string(next) or "nil")) - y_offset=1 - end - chkdir=middir1 - chkrely=midrely1 - --atprint("dir2 applied next pos:",minetest.pos_to_string(next),"(chkdir is ",chkdir,")") - end - --dir2??? - local cor2=advtrains.dirCoordSet(mid, middir1)--<<<< - if atround(cor2.x)==atround(prev.x) and atround(cor2.z)==atround(prev.z) then - next=advtrains.dirCoordSet(mid, middir2)--dir2 wird überprüft, alles gut. - if midrely2>=1 then - next.y=next.y+1 - --atprint("found midrely2 to be >=1: next is now "..(next and minetest.pos_to_string(next) or "nil")) - y_offset=1 - end - chkdir=middir2 - chkrely=midrely2 - --atprint(" dir2 applied next pos:",minetest.pos_to_string(next),"(chkdir is ",chkdir,")") - end - --atprint("dir applied next pos: "..(next and minetest.pos_to_string(next) or "nil").."(chkdir is "..(chkdir or "nil")..", y-offset "..y_offset..")") - --is there a next - if not next then - --atprint("in conway: no next rail(nil), returning!") - return nil - end - - local nextnode_ok, nextdir1, nextdir2, nextrely1, nextrely2, nextrailheight=advtrains.get_rail_info_at(advtrains.round_vector_floor_y(next), drives_on) - - --is it a rail? - if(not nextnode_ok) then - --atprint("in conway: next "..minetest.pos_to_string(next).." not a rail, trying one node below!") - next.y=next.y-1 - y_offset=y_offset-1 - - nextnode_ok, nextdir1, nextdir2, nextrely1, nextrely2, nextrailheight=advtrains.get_rail_info_at(advtrains.round_vector_floor_y(next), drives_on) - if(not nextnode_ok) then - --atprint("in conway: one below "..minetest.pos_to_string(next).." is not a rail either, returning!") - return nil - end - end - - --is this next rail connecting to the mid? - if not ( (((nextdir1+8)%16)==chkdir and nextrely1==chkrely-y_offset) or (((nextdir2+8)%16)==chkdir and nextrely2==chkrely-y_offset) ) then - --atprint("in conway: next "..minetest.pos_to_string(next).." not connecting, trying one node below!") - next.y=next.y-1 - y_offset=y_offset-1 - - nextnode_ok, nextdir1, nextdir2, nextrely1, nextrely2, nextrailheight=advtrains.get_rail_info_at(advtrains.round_vector_floor_y(next), drives_on) - if(not nextnode_ok) then - --atprint("in conway: (at connecting if check again) one below "..minetest.pos_to_string(next).." is not a rail either, returning!") - return nil - end - if not ( (((nextdir1+8)%16)==chkdir and nextrely1==chkrely) or (((nextdir2+8)%16)==chkdir and nextrely2==chkrely) ) then - --atprint("in conway: one below "..minetest.pos_to_string(next).." rail not connecting, returning!") - --atprint(" in order mid1,2,next1,2,chkdir "..middir1.." "..middir2.." "..nextdir1.." "..nextdir2.." "..chkdir) - return nil - end - end - - --atprint("conway found rail.") - return vector.add(advtrains.round_vector_floor_y(next), {x=0, y=nextrailheight, z=0}), chkdir -end ---TODO use this -function advtrains.oppd(dir) - return ((dir+8)%16) -end - function advtrains.round_vector_floor_y(vec) return {x=math.floor(vec.x+0.5), y=math.floor(vec.y), z=math.floor(vec.z+0.5)} end @@ -176,6 +85,20 @@ function advtrains.yawToAnyDir(yaw) end return min_conn end +function advtrains.yawToClosestConn(yaw, conns) + local min_connid, min_diff=1, 10 + for connid, conn in ipairs(conns) do + local uvec = vector.normalize(advtrains.dirToCoord(conn.c)) + local yaw1 = math.atan2(uvec.z, uvec.x) + local diff = advtrains.minAngleDiffRad(yaw, yaw1) + if diff < min_diff then + min_connid = connid + min_diff = diff + end + end + return min_connid +end + function advtrains.minAngleDiffRad(r1, r2) local pi, pi2 = math.pi, 2*math.pi @@ -300,3 +223,95 @@ end function advtrains.ms_to_kmh(speed) return speed * 3.6 end + +-- 4 possible inputs: +-- integer: just do that modulo calculation +-- table with c set: rotate c +-- table with tables: rotate each +-- table with integers: rotate each (probably no use case) +function advtrains.rotate_conn_by(conn, rotate) + if tonumber(conn) then + return (conn+rotate)%AT_CMAX + elseif conn.c then + return { c = (conn.c+rotate)%AT_CMAX, y = conn.y} + end + local tmp={} + for connid, data in ipairs(conn) do + tmp[connid]=advtrains.rotate_conn_by(data, rotate) + end + return tmp +end + +--TODO use this +function advtrains.oppd(dir) + return advtrains.rotate_conn_by(dir, AT_CMAX/2) +end +--conn_to_match like rotate_conn_by +--other_conns have to be a table of conn tables! +function advtrains.conn_matches_to(conn, other_conns) + if tonumber(conn) then + for connid, data in ipairs(other_conns) do + if advtrains.oppd(conn) == data.c then return connid end + end + return false + elseif conn.c then + for connid, data in ipairs(other_conns) do + local cmp = advtrains.oppd(conn) + if cmp.c == data.c and (cmp.y or 0) == (data.y or 0) then return connid end + end + return false + end + local tmp={} + for connid, data in ipairs(conn) do + local backmatch = advtrains.conn_matches_to(data, other_conns) + if backmatch then return backmatch, connid end --returns + end + return false +end + + +-- returns: , , , +function advtrains.get_adjacent_rail(this_posnr, this_conns_p, conn_idx, drives_on) + local this_pos = advtrains.round_vector_floor_y(this_posnr) + local this_conns = this_conns_p + if not this_conns then + _, this_conns = advtrains.get_rail_info_at(this_pos) + end + if not conn_idx then + for coni, _ in ipairs(this_conns) do + local adj_pos, adj_conn_idx, _, nry = advtrains.get_adjacent_rail(this_pos, this_conns, coni) + if adj_pos then return adj_pos,adj_conn_idx,coni,nry end + end + return nil + end + + local conn = this_conns[conn_idx] + local conn_y = conn.y or 0 + local adj_pos = advtrains.dirCoordSet(this_pos, conn.c); + + while conn_y>=1 do + conn_y = conn_y - 1 + adj_pos.y = adj_pos.y + 1 + end + + local nextnode_ok, nextconns, nextrail_y=advtrains.get_rail_info_at(adj_pos, drives_on) + 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) + if not nextnode_ok then + return nil + end + end + local adj_connid = advtrains.conn_matches_to({c=conn.c, y=conn_y}, nextconns) + if adj_connid then + return adj_pos, adj_connid, conn_idx, nextrail_y + end + return nil +end + +local connlku={[2]={2,1}, [3]={2,1,1}, [4]={2,1,4,3}} +function advtrains.get_matching_conn(conn, nconns) + return connlku[nconns][conn] +end + diff --git a/advtrains/init.lua b/advtrains/init.lua index 5975b81..e3a19e4 100644 --- a/advtrains/init.lua +++ b/advtrains/init.lua @@ -7,6 +7,9 @@ end --advtrains +--Constant for maximum connection value/division of the circle +AT_CMAX = 16 + advtrains = {trains={}, wagon_save={}, player_to_train_mapping={}} --pcall @@ -99,6 +102,13 @@ atwarn=function(t, ...) end sid=function(id) if id then return string.sub(id, -6) end end +--TEMP +atdebug=function(t, ...) + local text=advtrains.print_concat_table({t, ...}) + minetest.log("action", "[advtrains]"..text) + minetest.chat_send_all("[advtrains]"..text) + end + if minetest.settings:get_bool("advtrains_enable_debugging") then atprint=function(t, ...) local context=advtrains.atprint_context_tid or "" @@ -135,7 +145,7 @@ advtrains.meseconrules = {x=0, y=-2, z=0}} - +dofile(advtrains.modpath.."/path.lua") dofile(advtrains.modpath.."/trainlogic.lua") dofile(advtrains.modpath.."/trainhud.lua") dofile(advtrains.modpath.."/trackplacer.lua") diff --git a/advtrains/nodedb.lua b/advtrains/nodedb.lua index 2f014f8..763edbc 100644 --- a/advtrains/nodedb.lua +++ b/advtrains/nodedb.lua @@ -132,9 +132,12 @@ function ndb.get_node_raw(pos) end -function ndb.swap_node(pos, node) +function ndb.swap_node(pos, node, no_inval) minetest.swap_node(pos, node) ndb.update(pos, node) + if not no_inval then + advtrains.invalidate_all_paths(pos) + end end function ndb.update(pos, pnode) @@ -175,30 +178,15 @@ function advtrains.get_rail_info_at(pos, drives_on) local rdp=advtrains.round_vector_floor_y(pos) local node=ndb.get_node_or_nil(rdp) + if not node then return end - --still no node? - --advtrains.trackdb is nil when there's no data available. - if not node then - if advtrains.trackdb then - --try raildb (see trackdb_legacy.lua) - local dbe=(advtrains.trackdb[rdp.y] and advtrains.trackdb[rdp.y][rdp.x] and advtrains.trackdb[rdp.y][rdp.x][rdp.z]) - if dbe then - for tt,_ in pairs(drives_on) do - if not dbe.tracktype or tt==dbe.tracktype then - return true, dbe.conn1, dbe.conn2, dbe.rely1 or 0, dbe.rely2 or 0, dbe.railheight or 0 - end - end - end - end - return nil - end local nodename=node.name if(not advtrains.is_track_and_drives_on(nodename, drives_on)) then return false end - local conn1, conn2, rely1, rely2, railheight, tracktype=advtrains.get_track_connections(node.name, node.param2) + local conns, railheight, tracktype=advtrains.get_track_connections(node.name, node.param2) - return true, conn1, conn2, rely1, rely2, railheight + return true, conns, railheight end ndb.run_lbm = function(pos, node) diff --git a/advtrains/path.lua b/advtrains/path.lua new file mode 100644 index 0000000..506f27f --- /dev/null +++ b/advtrains/path.lua @@ -0,0 +1,82 @@ +-- path.lua +-- Functions for pathpredicting, put in a separate file. + +function advtrains.conway(midreal, prev, drives_on)--in order prev,mid,return + local mid=advtrains.round_vector_floor_y(midreal) + + local midnode_ok, midconns=advtrains.get_rail_info_at(mid, drives_on) + if not midnode_ok then + return nil + end + local pconnid + for connid, conn in ipairs(midconns) do + local tps = advtrains.dirCoordSet(mid, conn.c) + if tps.x==prev.x and tps.z==prev.z then + pconnid=connid + end + end + local nconnid = advtrains.get_matching_conn(pconnid, #midconns) + + local next, next_connid, _, nextrailheight = advtrains.get_adjacent_rail(mid, midconns, nconnid, drives_on) + if not next then + return nil + end + return vector.add(advtrains.round_vector_floor_y(next), {x=0, y=nextrailheight, z=0}), midconns[nconnid].c +end + +--about regular: Used by 1. to ensure path gets generated far enough, since end index is not known at this time. +function advtrains.pathpredict(id, train, regular) + --TODO duplicate code under 5b. + local path_pregen=10 + + local gen_front= path_pregen + local gen_back= - train.trainlen - path_pregen + if regular then + gen_front=math.max(train.index, train.detector_old_index) + path_pregen + gen_back=math.min(train.end_index, train.detector_old_end_index) - path_pregen + end + + local maxn=train.path_extent_max or 0 + while maxn < gen_front do--pregenerate + local conway + if train.max_index_on_track == maxn then + --atprint("maxn conway for ",maxn,train.path[maxn],maxn-1,train.path[maxn-1]) + conway=advtrains.conway(train.path[maxn], train.path[maxn-1], train.drives_on) + end + if conway then + train.path[maxn+1]=conway + train.max_index_on_track=maxn+1 + else + --do as if nothing has happened and preceed with path + --but do not update max_index_on_track + atprint("over-generating path max to index ",(maxn+1)," (position ",train.path[maxn]," )") + train.path[maxn+1]=vector.add(train.path[maxn], vector.subtract(train.path[maxn], train.path[maxn-1])) + end + train.path_dist[maxn]=vector.distance(train.path[maxn+1], train.path[maxn]) + maxn=maxn+1 + end + train.path_extent_max=maxn + + local minn=train.path_extent_min or -1 + while minn > gen_back do + local conway + if train.min_index_on_track == minn then + --atprint("minn conway for ",minn,train.path[minn],minn+1,train.path[minn+1]) + conway=advtrains.conway(train.path[minn], train.path[minn+1], train.drives_on) + end + if conway then + train.path[minn-1]=conway + train.min_index_on_track=minn-1 + else + --do as if nothing has happened and preceed with path + --but do not update min_index_on_track + atprint("over-generating path min to index ",(minn-1)," (position ",train.path[minn]," )") + train.path[minn-1]=vector.add(train.path[minn], vector.subtract(train.path[minn], train.path[minn+1])) + end + train.path_dist[minn-1]=vector.distance(train.path[minn], train.path[minn-1]) + minn=minn-1 + end + train.path_extent_min=minn + if not train.min_index_on_track then train.min_index_on_track=-1 end + if not train.max_index_on_track then train.max_index_on_track=0 end +end diff --git a/advtrains/signals.lua b/advtrains/signals.lua index a42f5e7..b01314e 100644 --- a/advtrains/signals.lua +++ b/advtrains/signals.lua @@ -37,12 +37,12 @@ for r,f in pairs({on={as="off", ls="green", als="red"}, off={as="on", ls="red", mesecons = {effector = { rules=advtrains.meseconrules, ["action_"..f.as] = function (pos, node) - advtrains.ndb.swap_node(pos, {name = "advtrains:retrosignal_"..f.as..rotation, param2 = node.param2}) + advtrains.ndb.swap_node(pos, {name = "advtrains:retrosignal_"..f.as..rotation, param2 = node.param2}, true) end }}, on_rightclick=function(pos, node, player) if minetest.check_player_privs(player:get_player_name(), {train_operator=true}) then - advtrains.ndb.swap_node(pos, {name = "advtrains:retrosignal_"..f.as..rotation, param2 = node.param2}) + advtrains.ndb.swap_node(pos, {name = "advtrains:retrosignal_"..f.as..rotation, param2 = node.param2}, true) end end, }) @@ -72,20 +72,20 @@ for r,f in pairs({on={as="off", ls="green", als="red"}, off={as="on", ls="red", mesecons = {effector = { rules=advtrains.meseconrules, ["action_"..f.as] = function (pos, node) - advtrains.ndb.swap_node(pos, {name = "advtrains:signal_"..f.as..rotation, param2 = node.param2}) + advtrains.ndb.swap_node(pos, {name = "advtrains:signal_"..f.as..rotation, param2 = node.param2}, true) end }}, luaautomation = { getstate = f.ls, setstate = function(pos, node, newstate) if newstate == f.als then - advtrains.ndb.swap_node(pos, {name = "advtrains:signal_"..f.as..rotation, param2 = node.param2}) + advtrains.ndb.swap_node(pos, {name = "advtrains:signal_"..f.as..rotation, param2 = node.param2}, true) end end, }, on_rightclick=function(pos, node, player) if minetest.check_player_privs(player:get_player_name(), {train_operator=true}) then - advtrains.ndb.swap_node(pos, {name = "advtrains:signal_"..f.as..rotation, param2 = node.param2}) + advtrains.ndb.swap_node(pos, {name = "advtrains:signal_"..f.as..rotation, param2 = node.param2}, true) end end, }) @@ -121,20 +121,20 @@ for r,f in pairs({on={as="off", ls="green", als="red"}, off={as="on", ls="red", mesecons = {effector = { rules = mrules_wallsignal, ["action_"..f.as] = function (pos, node) - advtrains.ndb.swap_node(pos, {name = "advtrains:signal_wall_"..loc.."_"..f.as, param2 = node.param2}) + advtrains.ndb.swap_node(pos, {name = "advtrains:signal_wall_"..loc.."_"..f.as, param2 = node.param2}, true) end }}, luaautomation = { getstate = f.ls, setstate = function(pos, node, newstate) if newstate == f.als then - advtrains.ndb.swap_node(pos, {name = "advtrains:signal_wall_"..loc.."_"..f.as, param2 = node.param2}) + advtrains.ndb.swap_node(pos, {name = "advtrains:signal_wall_"..loc.."_"..f.as, param2 = node.param2}, true) end end, }, on_rightclick=function(pos, node, player) if minetest.check_player_privs(player:get_player_name(), {train_operator=true}) then - advtrains.ndb.swap_node(pos, {name = "advtrains:signal_wall_"..loc.."_"..f.as, param2 = node.param2}) + advtrains.ndb.swap_node(pos, {name = "advtrains:signal_wall_"..loc.."_"..f.as, param2 = node.param2}, true) end end, }) @@ -167,20 +167,20 @@ minetest.register_node("advtrains:across_off", { mesecons = {effector = { rules = advtrains.meseconrules, action_on = function (pos, node) - advtrains.ndb.swap_node(pos, {name = "advtrains:across_on", param2 = node.param2}) + advtrains.ndb.swap_node(pos, {name = "advtrains:across_on", param2 = node.param2}, true) end }}, luaautomation = { getstate = "off", setstate = function(pos, node, newstate) if newstate == "on" then - advtrains.ndb.swap_node(pos, {name = "advtrains:across_on", param2 = node.param2}) + advtrains.ndb.swap_node(pos, {name = "advtrains:across_on", param2 = node.param2}, true) end end, }, on_rightclick=function(pos, node, player) if minetest.check_player_privs(player:get_player_name(), {train_operator=true}) then - advtrains.ndb.swap_node(pos, {name = "advtrains:across_on", param2 = node.param2}) + advtrains.ndb.swap_node(pos, {name = "advtrains:across_on", param2 = node.param2}, true) end end, }) @@ -208,20 +208,20 @@ minetest.register_node("advtrains:across_on", { mesecons = {effector = { rules = advtrains.meseconrules, action_off = function (pos, node) - advtrains.ndb.swap_node(pos, {name = "advtrains:across_off", param2 = node.param2}) + advtrains.ndb.swap_node(pos, {name = "advtrains:across_off", param2 = node.param2}, true) end }}, luaautomation = { getstate = "on", setstate = function(pos, node, newstate) if newstate == "off" then - advtrains.ndb.swap_node(pos, {name = "advtrains:across_off", param2 = node.param2}) + advtrains.ndb.swap_node(pos, {name = "advtrains:across_off", param2 = node.param2}, true) end end, }, on_rightclick=function(pos, node, player) if minetest.check_player_privs(player:get_player_name(), {train_operator=true}) then - advtrains.ndb.swap_node(pos, {name = "advtrains:across_off", param2 = node.param2}) + advtrains.ndb.swap_node(pos, {name = "advtrains:across_off", param2 = node.param2}, true) end end, }) diff --git a/advtrains/trackplacer.lua b/advtrains/trackplacer.lua index c484440..c61bbb4 100644 --- a/advtrains/trackplacer.lua +++ b/advtrains/trackplacer.lua @@ -71,31 +71,35 @@ end local function istrackandbc(pos_p, conn) local tpos = pos_p - local cnode=minetest.get_node(advtrains.dirCoordSet(tpos, conn)) - local bconn=(conn+8)%16 + local cnode=minetest.get_node(advtrains.dirCoordSet(tpos, conn.c)) if advtrains.is_track_and_drives_on(cnode.name, advtrains.all_tracktypes) then - local cconn1, cconn2=advtrains.get_track_connections(cnode.name, cnode.param2) - return cconn1==bconn or cconn2==bconn + 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)) - bconn=(conn+8)%16 + cnode=minetest.get_node(advtrains.dirCoordSet(tpos, conn.c)) if advtrains.is_track_and_drives_on(cnode.name, advtrains.all_tracktypes) then - local cconn1, cconn2=advtrains.get_track_connections(cnode.name, cnode.param2) - return cconn1==bconn or cconn2==bconn + local cconns=advtrains.get_track_connections(cnode.name, cnode.param2) + return advtrains.conn_matches_to(conn, cconns) end return false end function tp.find_already_connected(pos) local dnode=minetest.get_node(pos) - local dconn1, dconn2=advtrains.get_track_connections(dnode.name, dnode.param2) - if istrackandbc(pos, dconn1) and istrackandbc(pos, dconn2) then return dconn1, dconn2 - elseif istrackandbc(pos, dconn1) then return dconn1 - elseif istrackandbc(pos, dconn2) then return dconn2 + 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 + end + end end - return nil + return found_conn end function tp.rail_and_can_be_bent(originpos, conn) local pos=advtrains.dirCoordSet(originpos, conn) @@ -105,16 +109,15 @@ function tp.rail_and_can_be_bent(originpos, conn) return false end local ndef=minetest.registered_nodes[node.name] - local nnpref = ndef and ndef.nnpref + 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. - local bconn=(conn+8)%16 if advtrains.is_track_and_drives_on(node.name, advtrains.all_tracktypes) then - local cconn1, cconn2=advtrains.get_track_connections(node.name, node.param2) - return cconn1==bconn or cconn2==bconn + local cconns=advtrains.get_track_connections(node.name, node.param2) + return advtrains.conn_matches_to(conn, cconns) end end --rail at other end? @@ -135,16 +138,16 @@ function tp.rail_and_can_be_bent(originpos, conn) end function tp.bend_rail(originpos, conn) local pos=advtrains.dirCoordSet(originpos, conn) - local newdir=(conn+8)%16 + local newdir=advtrains.oppd(conn) local node=minetest.get_node(pos) local ndef=minetest.registered_nodes[node.name] - local nnpref = ndef and ndef.nnpref + 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 conn1, conn2=advtrains.get_track_connections(node.name, node.param2) - if newdir==conn1 or newdir==conn2 then + local conns=advtrains.get_track_connections(node.name, node.param2) + if advtrains.conn_matches_to(conn, conns) then return end --rail at other end? @@ -309,6 +312,7 @@ minetest.register_craftitem("advtrains:trackworker",{ if not name then return end + local has_aux1_down = placer:get_player_control().aux1 if pointed_thing.type=="node" then local pos=pointed_thing.under if advtrains.is_protected(pos, name) then @@ -319,6 +323,13 @@ minetest.register_craftitem("advtrains:trackworker",{ --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 + + if 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}) + return + end local nnprefix, suffix, rotation=string.match(node.name, "^(.+)_([^_]+)(_[^_]+)$") --atprint(node.name.."\npattern recognizes:"..nodeprefix.." / "..railtype.." / "..rotation) diff --git a/advtrains/tracks.lua b/advtrains/tracks.lua index 1cbbc0b..6d218b1 100644 --- a/advtrains/tracks.lua +++ b/advtrains/tracks.lua @@ -34,90 +34,80 @@ vert2={ advtrains.all_tracktypes={} --definition preparation -local function conns(c1, c2, r1, r2, rh, rots) return {conn1=c1, conn2=c2, rely1=r1, rely2=r2, railheight=rh} end +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(0,8), - cr=conns(0,7), - swlst=conns(0,8), - swlcr=conns(0,7), - swrst=conns(0,8), - swrcr=conns(0,9), - }, - description={ - st="straight", - cr="curve", - swlst="left switch (straight)", - swlcr="left switch (curve)", - swrst="right switch (straight)", - swrcr="right switch (curve)", - }, - switch={ - swlst="swlcr", - swlcr="swlst", - swrst="swrcr", - swrcr="swrst", - }, - switchmc={ - swlst="on", - swlcr="off", - swrst="on", - swrcr="off", - }, - switchst={ - swlst="st", - swlcr="cr", - swrst="st", - swrcr="cr", + 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 = "swlcr", + switchmc = "on", + switchst = "st", + }, + swlcr={ + conns = conns3(0,7,8), + desc = "left switch (curve)", + trackworker = "swrcr", + switchalt = "swlst", + switchmc = "off", + switchst = "cr", + }, + swrst={ + conns = conns3(0,8,9), + desc = "right switch (straight)", + trackworker = "st", + switchalt = "swrcr", + switchmc = "on", + switchst = "st", + }, + swrcr={ + conns = conns3(0,9,8), + desc = "right switch (curve)", + trackworker = "st", + switchalt = "swrst", + switchmc = "off", + switchst = "cr", + }, }, regtp=true, - trackplacer={ - st=true, - cr=true, - }, - tpsingle={ - st=true, - }, tpdefault="st", trackworker={ ["swrcr"]="st", ["swrst"]="st", - ["st"]="cr", ["cr"]="swlst", ["swlcr"]="swrcr", ["swlst"]="swrst", }, rotation={"", "_30", "_45", "_60"}, - slopenodes={}, - increativeinv={}, } advtrains.ap.t_30deg_slope={ regstep=1, variant={ - vst1=conns(8,0,0,0.5,0.25), - vst2=conns(8,0,0.5,1,0.75), - vst31=conns(8,0,0,0.33,0.16), - vst32=conns(8,0,0.33,0.66,0.5), - vst33=conns(8,0,0.66,1,0.83), - }, - description={ - vst1="steep uphill 1/2", - vst2="steep uphill 2/2", - vst31="uphill 1/3", - vst32="uphill 2/3", - vst33="uphill 3/3", + 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}, }, - switch={}, - switchmc={}, - switchst={}, regsp=true, - slopenodes={ - vst1=true, vst2=true, - vst31=true, vst32=true, vst33=true, - }, slopeplacer={ [2]={"vst1", "vst2"}, [3]={"vst31", "vst32", "vst33"}, @@ -134,125 +124,91 @@ advtrains.ap.t_30deg_slope={ advtrains.ap.t_30deg_straightonly={ regstep=1, variant={ - st=conns(0,8), - }, - description={ - st="straight", - }, - switch={ - }, - switchmc={ + st={ + conns = conns(0,8), + desc = "straight", + tpdouble = true, + tpsingle = true, + trackworker = "st", + }, }, regtp=true, - trackplacer={ - }, - tpsingle={ - }, tpdefault="st", - trackworker={ - ["st"]="st", - }, - trackplacer={ - st=true, - }, - tpsingle={ - st=true, - }, - slopenodes={}, rotation={"", "_30", "_45", "_60"}, - increativeinv={"st"}, } advtrains.ap.t_30deg_straightonly_noplacer={ regstep=1, variant={ - st=conns(0,8), - }, - description={ - st="straight", - }, - switch={ - }, - switchmc={ - }, - regtp=false, - trackplacer={ - }, - tpsingle={ + st={ + conns = conns(0,8), + desc = "straight", + tpdouble = true, + tpsingle = true, + trackworker = "st", + }, }, tpdefault="st", - trackworker={ - ["st"]="st", - }, - trackplacer={ - st=true, - }, - tpsingle={ - st=true, - }, - slopenodes={}, rotation={"", "_30", "_45", "_60"}, - increativeinv={"st"}, } advtrains.ap.t_45deg={ regstep=2, variant={ - st=conns(0,8), - cr=conns(0,6), - swlst=conns(0,8), - swlcr=conns(0,6), - swrst=conns(0,8), - swrcr=conns(0,10), - vst1=conns(8,0,0,0.5,0.25), - vst2=conns(8,0,0.5,1,0.75), - }, - description={ - st="straight", - cr="curve", - swlst="left switch (straight)", - swlcr="left switch (curve)", - swrst="right switch (straight)", - swrcr="right switch (curve)", - vst1="vertical lower node", - vst2="vertical upper node", - }, - switch={ - swlst="swlcr", - swlcr="swlst", - swrst="swrcr", - swrcr="swrst", - }, - switchmc={ - swlst="on", - swlcr="off", - swrst="on", - swrcr="off", - }, - switchst={ - swlst="st", - swlcr="cr", - swrst="st", - swrcr="cr", + 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 = "swlcr", + switchmc = "on", + switchst = "st", + }, + swlcr={ + conns = conns3(0,6,8), + desc = "left switch (curve)", + trackworker = "swrcr", + switchalt = "swlst", + switchmc = "off", + switchst = "cr", + }, + swrst={ + conns = conns3(0,8,10), + desc = "right switch (straight)", + trackworker = "st", + switchalt = "swrcr", + switchmc = "on", + switchst = "st", + }, + swrcr={ + conns = conns3(0,10,8), + desc = "right switch (curve)", + trackworker = "st", + switchalt = "swrst", + switchmc = "off", + switchst = "cr", + }, }, regtp=true, - trackplacer={ - st=true, - cr=true, - }, - tpsingle={ - st=true, - }, tpdefault="st", trackworker={ ["swrcr"]="st", ["swrst"]="st", - ["st"]="cr", ["cr"]="swlst", ["swlcr"]="swrcr", ["swlst"]="swrst", }, - slopenodes={}, - rotation={"", "_45"}, - increativeinv={vst1=true, vst2=true} + rotation={"", "_30", "_45", "_60"}, } advtrains.trackpresets = advtrains.ap @@ -269,98 +225,23 @@ advtrains.trackpresets = advtrains.ap (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=, y=} +The table "at_conns" consists of {, ...} +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 +]] + function advtrains.register_tracks(tracktype, def, preset) - local function make_switchfunc(suffix_target, mesecon_state, is_state) - local rcswitchfunc=function(pos, node, player) - if minetest.check_player_privs(player:get_player_name(), {train_operator=true}) then - advtrains.ndb.swap_node(pos, {name=def.nodename_prefix.."_"..suffix_target, param2=node.param2}) - advtrains.invalidate_all_paths(pos) - end - end - local switchfunc=function(pos, node, newstate) - if newstate~=is_state then - advtrains.ndb.swap_node(pos, {name=def.nodename_prefix.."_"..suffix_target, param2=node.param2}) - advtrains.invalidate_all_paths(pos) - end - end - local mesec - if mesecon_state then -- if mesecons is not wanted, do not. - mesec = {effector = { - ["action_"..mesecon_state] = switchfunc, - rules=advtrains.meseconrules - }} - end - return rcswitchfunc, mesec, - { - getstate = is_state, - setstate = switchfunc, - } - end - local function make_overdef(suffix, rotation, conns, rcswitchfunc, mesecontbl, luaautomation, in_creative_inv, drop_slope) - local img_suffix=suffix..rotation - return { - 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}, - --inventory_image = def.texture_prefix.."_"..img_suffix..".png", - --wield_image = def.texture_prefix.."_"..img_suffix..".png", - description=def.description.."("..preset.description[suffix]..rotation..")", - connect1=conns.conn1, - connect2=conns.conn2, - rely1=conns.rely1 or 0, - rely2=conns.rely2 or 0, - railheight=conns.railheight or 0, - - on_rightclick=rcswitchfunc, - groups = { - attached_node=1, - ["advtrains_track_"..tracktype]=1, - save_in_at_nodedb=1, - dig_immediate=2, - not_in_creative_inventory=(not in_creative_inv and 1 or nil), - not_blocking_trains=1, - }, - mesecons=mesecontbl, - luaautomation=luaautomation, - drop = (drop_slope and def.nodename_prefix.."_slopeplacer" or def.nodename_prefix.."_placer"), - } - end - local function cycle_conns(conns, rotid) - local add=(rotid-1)*preset.regstep - return { - conn1=(conns.conn1+add)%16, - conn2=(conns.conn2+add)%16, - rely1=conns.rely1 or 0, - rely2=conns.rely2 or 0, - railheight=conns.railheight or 0, - } - end - local common_def=advtrains.merge_tables({ - description = def.description, - drawtype = "mesh", - paramtype="light", - paramtype2="facedir", - walkable = false, - selection_box = { - type = "fixed", - fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2}, - }, - rely1=0, - rely2=0, - railheight=0, - drop=def.nodename_prefix.."_placer", - can_dig=function(pos) - return not advtrains.get_train_at_pos(pos) - end, - after_dig_node=function(pos) - advtrains.ndb.update(pos) - end, - after_place_node=function(pos) - advtrains.ndb.update(pos) - end, - nnpref = def.nodename_prefix, - }, def.common or {}) - --make trackplacer base def 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) @@ -368,45 +249,106 @@ function advtrains.register_tracks(tracktype, def, preset) if preset.regsp then advtrains.slope.register_placer(def, preset) end - for suffix, conns in pairs(preset.variant) do + 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 rcswitchfunc, mesecontbl, luaautomation - if preset.switch[suffix] then - rcswitchfunc, mesecontbl, luaautomation=make_switchfunc(preset.switch[suffix]..rotation, preset.switchmc[suffix], preset.switchst[suffix]) + 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/2, -1/2, 1/2, -1/2+1/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=1, + ["advtrains_track_"..tracktype]=1, + save_in_at_nodedb=1, + dig_immediate=2, + not_in_creative_inventory=1, + not_blocking_trains=1, + }, + + can_dig=function(pos) + return not advtrains.get_train_at_pos(pos) + end, + 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) + + if var.switchalt and var.switchst then + local switchfunc=function(pos, node, newstate) + if newstate~=var.switchst then + advtrains.ndb.swap_node(pos, {name=def.nodename_prefix.."_"..var.switchalt..rotation, param2=node.param2}) + advtrains.invalidate_all_paths(pos) + end + end + ndef.on_rightclick = function(pos, node, player) + if minetest.check_player_privs(player:get_player_name(), {train_operator=true}) then + switchfunc(pos, node) + end + end + if var.switchmc then + ndef.mesecons = {effector = { + ["action_"..var.switchmc] = switchfunc, + rules=advtrains.meseconrules + }} + end + ndef.luaautomation = { + 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) - minetest.register_node(":"..def.nodename_prefix.."_"..suffix..rotation, advtrains.merge_tables( - common_def, - make_overdef( - suffix, rotation, - cycle_conns(conns, rotid), - rcswitchfunc, mesecontbl, luaautomation, preset.increativeinv[suffix], preset.slopenodes[suffix] - ), - adef - ) - ) + minetest.register_node(":"..def.nodename_prefix.."_"..suffix..rotation, ndef) --trackplacer if preset.regtp then - if preset.trackplacer[suffix] then - advtrains.trackplacer.add_double_conn(def.nodename_prefix, suffix, rotation, cycle_conns(conns, rotid)) + 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 preset.tpsingle[suffix] then - advtrains.trackplacer.add_single_conn(def.nodename_prefix, suffix, rotation, cycle_conns(conns, rotid)) + 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, preset.trackworker[suffix]) + 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 @@ -430,7 +372,7 @@ 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 0, 8, 0, 0, 0 end + 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 @@ -442,7 +384,7 @@ function advtrains.get_track_connections(name, param2) tracktype=tt end end - return (nodedef.connect1 + 4 * noderot)%16, (nodedef.connect2 + 4 * noderot)%16, nodedef.rely1 or 0, nodedef.rely2 or 0, nodedef.railheight or 0, tracktype + return advtrains.rotate_conn_by(nodedef.at_conns, noderot*AT_CMAX/4), (nodedef.at_rail_y or 0), tracktype end --detector code diff --git a/advtrains/trainlogic.lua b/advtrains/trainlogic.lua index d51f1eb..76b0d1d 100644 --- a/advtrains/trainlogic.lua +++ b/advtrains/trainlogic.lua @@ -428,63 +428,6 @@ function advtrains.train_step_a(id, train, dtime) end end ---about regular: Used by 1. to ensure path gets generated far enough, since end index is not known at this time. -function advtrains.pathpredict(id, train, regular) - --TODO duplicate code under 5b. - local path_pregen=10 - - local gen_front= path_pregen - local gen_back= - train.trainlen - path_pregen - if regular then - gen_front=math.max(train.index, train.detector_old_index) + path_pregen - gen_back=math.min(train.end_index, train.detector_old_end_index) - path_pregen - end - - local maxn=train.path_extent_max or 0 - while maxn < gen_front do--pregenerate - local conway - if train.max_index_on_track == maxn then - --atprint("maxn conway for ",maxn,train.path[maxn],maxn-1,train.path[maxn-1]) - conway=advtrains.conway(train.path[maxn], train.path[maxn-1], train.drives_on) - end - if conway then - train.path[maxn+1]=conway - train.max_index_on_track=maxn+1 - else - --do as if nothing has happened and preceed with path - --but do not update max_index_on_track - atprint("over-generating path max to index ",(maxn+1)," (position ",train.path[maxn]," )") - train.path[maxn+1]=vector.add(train.path[maxn], vector.subtract(train.path[maxn], train.path[maxn-1])) - end - train.path_dist[maxn]=vector.distance(train.path[maxn+1], train.path[maxn]) - maxn=maxn+1 - end - train.path_extent_max=maxn - - local minn=train.path_extent_min or -1 - while minn > gen_back do - local conway - if train.min_index_on_track == minn then - --atprint("minn conway for ",minn,train.path[minn],minn+1,train.path[minn+1]) - conway=advtrains.conway(train.path[minn], train.path[minn+1], train.drives_on) - end - if conway then - train.path[minn-1]=conway - train.min_index_on_track=minn-1 - else - --do as if nothing has happened and preceed with path - --but do not update min_index_on_track - atprint("over-generating path min to index ",(minn-1)," (position ",train.path[minn]," )") - train.path[minn-1]=vector.add(train.path[minn], vector.subtract(train.path[minn], train.path[minn+1])) - end - train.path_dist[minn-1]=vector.distance(train.path[minn], train.path[minn-1]) - minn=minn-1 - end - train.path_extent_min=minn - if not train.min_index_on_track then train.min_index_on_track=-1 end - if not train.max_index_on_track then train.max_index_on_track=0 end -end - function advtrains.train_step_b(id, train, dtime) diff --git a/advtrains/wagons.lua b/advtrains/wagons.lua index 3e73f39..cc1f003 100644 --- a/advtrains/wagons.lua +++ b/advtrains/wagons.lua @@ -931,8 +931,13 @@ function advtrains.register_wagon(sysname_p, prototype, desc, inv_img) minetest.record_protection_violation(pointed_thing.under, placer:get_player_name()) return end - local conn1=advtrains.get_track_connections(node.name, node.param2) - local id=advtrains.create_new_train_at(pointed_thing.under, advtrains.dirCoordSet(pointed_thing.under, conn1)) + local tconns=advtrains.get_track_connections(node.name, node.param2) + local yaw = placer:get_look_horizontal() + (math.pi/2) + local plconnid = advtrains.yawToClosestConn(yaw, tconns) + + local prevpos = advtrains.get_adjacent_rail(pointed_thing.under, tconns, plconnid, prototype.drives_on) + if not prevpos then return end + local id=advtrains.create_new_train_at(pointed_thing.under, prevpos) local ob=minetest.add_entity(pointed_thing.under, sysname) if not ob then -- cgit v1.2.3