From 3420a1a912b83917d6674c1fa14a3f884eb25bc2 Mon Sep 17 00:00:00 2001 From: orwell96 Date: Wed, 21 Feb 2018 19:32:41 +0100 Subject: Restructure path system The changes to the path system have not yet been integrated... --- advtrains/helpers.lua | 20 ++++-- advtrains/init.lua | 4 ++ advtrains/path.lua | 164 +++++++++++++++++++++++++++++++++++++++++++---- advtrains/trainlogic.lua | 40 ++++++++++-- 4 files changed, 204 insertions(+), 24 deletions(-) diff --git a/advtrains/helpers.lua b/advtrains/helpers.lua index 5f5521f..5dbfd8d 100644 --- a/advtrains/helpers.lua +++ b/advtrains/helpers.lua @@ -28,9 +28,12 @@ function advtrains.dirCoordSet(coord, dir) end return {x=coord.x+x, y=coord.y, z=coord.z+z} end +advtrains.pos_add_dir = advtrains.dirCoordSet + function advtrains.dirToCoord(dir) return advtrains.dirCoordSet({x=0, y=0, z=0}, dir) end +advtrains.dir_to_vector = advtrains.dirToCoord function advtrains.maxN(list, expectstart) local n=expectstart or 0 @@ -51,6 +54,8 @@ end function atround(number) return math.floor(number+0.5) end +atfloor = math.floor + 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)} @@ -60,8 +65,8 @@ function advtrains.yawToDirection(yaw, conn1, conn2) if not conn1 or not conn2 then error("given nil to yawToDirection: conn1="..(conn1 or "nil").." conn2="..(conn1 or "nil")) end - local yaw1=math.pi*(conn1/8) - local yaw2=math.pi*(conn2/8) + local yaw1 = advtrains.dir_to_angle(conn1) + local yaw2 = advtrains.dir_to_angle(conn2) local adiff1 = advtrains.minAngleDiffRad(yaw, yaw1) local adiff2 = advtrains.minAngleDiffRad(yaw, yaw2) @@ -75,8 +80,7 @@ end function advtrains.yawToAnyDir(yaw) local min_conn, min_diff=0, 10 for conn, vec in pairs(advtrains.dir_trans_tbl) do - local uvec = vector.normalize(advtrains.dirToCoord(conn)) - local yaw1 = math.atan2(uvec.z, uvec.x) + local yaw1 = advtrains.dir_to_angle(conn) local diff = advtrains.minAngleDiffRad(yaw, yaw1) if diff < min_diff then min_conn = conn @@ -88,8 +92,7 @@ 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 yaw1 = advtrains.dir_to_angle(conn.c) local diff = advtrains.minAngleDiffRad(yaw, yaw1) if diff < min_diff then min_connid = connid @@ -99,6 +102,11 @@ function advtrains.yawToClosestConn(yaw, conns) return min_connid end +function advtrains.dir_to_angle(dir) + local uvec = vector.normalize(advtrains.dirToCoord()) + local yaw1 = math.atan2(uvec.z, -uvec.x) +end + function advtrains.minAngleDiffRad(r1, r2) local pi, pi2 = math.pi, 2*math.pi diff --git a/advtrains/init.lua b/advtrains/init.lua index 65e5048..0043c22 100644 --- a/advtrains/init.lua +++ b/advtrains/init.lua @@ -184,6 +184,10 @@ function advtrains.avt_load() if tbl.version then --congrats, we have the new save format. advtrains.trains = tbl.trains + --Save the train id into the train table to avoid having to pass id around + for id, train in pairs(advtrains.trains) do + train.id = id + end advtrains.wagon_save = tbl.wagon_save advtrains.player_to_train_mapping = tbl.ptmap or {} advtrains.ndb.load_data(tbl.ndb) diff --git a/advtrains/path.lua b/advtrains/path.lua index 506f27f..07b60b2 100644 --- a/advtrains/path.lua +++ b/advtrains/path.lua @@ -24,17 +24,7 @@ function advtrains.conway(midreal, prev, drives_on)--in order prev,mid,return 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 +function advtrains.pathpredict(id, train, gen_front, gen_back) local maxn=train.path_extent_max or 0 while maxn < gen_front do--pregenerate @@ -80,3 +70,155 @@ function advtrains.pathpredict(id, train, regular) 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 + +-- Naming conventions: +-- 'index' - An index of the train.path table. +-- 'offset' - A value in meters that determines how far on the path to walk relative to a certain index +-- 'n' - Referring or pointing towards the 'next' path item, the one with index+1 +-- 'p' - Referring or pointing towards the 'prev' path item, the one with index-1 +-- 'f' - Referring to the positive end of the path (the end with the higher index) +-- 'b' - Referring to the negative end of the path (the end with the lower index) + +-- New path structure of trains: +--Tables: +-- path - path positions. 'indices' are relative to this. At the moment, at.round_vector_floor_y(path[i]) +-- is the node this item corresponds to, however, this will change in the future. +-- path_node - (reserved) +-- path_cn - Connid of the current node that points towards path[i+1] +-- path_cp - Connid of the current node that points towards path[i-1] +-- When the day comes on that path!=node, these will only be set if this index represents a transition between rail nodes +-- path_dist - The distance (in meters) between this (path[i]) and the next (path[i+1]) item of the path +-- path_dir - The direction of this path item's transition to the next path item, which is the angle of conns[path_cn[i]].c +--Variables: +-- path_ext_f/b - how far path[i] is set +-- path_trk_f/b - how far the path extends along a track. beyond those values, paths are generated in a straight line. +-- path_req_f/b - how far path items were requested in the last step + +-- 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) + if not node_ok then + return node_ok + end + local mconnid = advtrains.get_matching_conn(connid, #conns) + train.index = rel_index + train.path = { [0] = { x=posr.x, y=posr.y+rhe, z=posr.z } } + train.path_cn = { [0] = connid } + train.path_cp = { [0] = mconnid } + train.path_dist = {} + + train.path_dir = { + [ 0] = conns[connid], + [-1] = conns[mconnid] + } + + train.path_ext_f=0 + train.path_ext_b=0 + train.path_trk_f=0 + train.path_trk_b=0 + train.path_req_f=0 + train.path_req_b=0 + +end + +-- Function to get path entry at a position. This function will automatically calculate more of the path when required. +-- returns: pos, on_track +function advtrains.path_get(train, index) + if index ~= atfloor(index) then + error("For train "..train.id..": Called path_get() but index="..index.." is not a round number") + end + while index > train.path_ext_f do + local pos = train.path[train.path_ext_f] + local connid = train.path_cn[train.path_ext_f] + local node_ok, this_conns, adj_pos, adj_connid, conn_idx, nextrail_y + if train.path_ext_f == train.path_trk_f then + node_ok, this_conns = advtrains.get_rail_info_at(this_pos) + if not node_ok then error("For train "..train.id..": Path item "..train.path_ext_f.." on-track but not a valid node!") end + adj_pos, adj_connid, conn_idx, nextrail_y = advtrains.get_adjacent_rail(pos, this_conns, connid, train.drives_on) + end + train.path_ext_f = train.path_ext_f + 1 + if adj_pos then + adj_pos.y = adj_pos.y + nextrail_y + train.path_cp[train.path_ext_f] = adj_connid + local mconnid = advtrains.get_matching_conn(adj_connid) + train.path_cn[train.path_ext_f] = mconnid + train.path_dir[train.path_ext_f] = this_conns[mconnid] + train.path_trk_f = train.path_ext_f + else + -- off-track fallback behavior + adj_pos = advtrains.pos_add_dir(pos, train.path_dir[train.path_ext_f-1]) + train.path_dir[train.path_ext_f] = train.path_dir[train.path_ext_f-1] + end + train.path[train.path_ext_f] = adj_pos + train.path_dist[train.path_ext_f - 1] = vector.distance(pos, adj_pos) + end + while index < train.path_ext_b do + local pos = train.path[train.path_ext_b] + local connid = train.path_cp[train.path_ext_b] + local node_ok, this_conns, adj_pos, adj_connid, conn_idx, nextrail_y + if train.path_ext_b == train.path_trk_b then + node_ok, this_conns = advtrains.get_rail_info_at(this_pos) + if not node_ok then error("For train "..train.id..": Path item "..train.path_ext_f.." on-track but not a valid node!") end + adj_pos, adj_connid, conn_idx, nextrail_y = advtrains.get_adjacent_rail(pos, this_conns, connid, train.drives_on) + end + train.path_ext_b = train.path_ext_b - 1 + if adj_pos then + adj_pos.y = adj_pos.y + nextrail_y + train.path_cp[train.path_ext_b] = adj_connid + local mconnid = advtrains.get_matching_conn(adj_connid) + train.path_cn[train.path_ext_b] = mconnid + train.path_dir[train.path_ext_b] = advtrains.oppd(this_conns[mconnid]) --we need to rotate this here so that it points in positive path direction + train.path_trk_b = train.path_ext_b + else + -- off-track fallback behavior + adj_pos = advtrains.pos_add_dir(pos, train.path_dir[train.path_ext_b-1]) + train.path_dir[train.path_ext_b] = train.path_dir[train.path_ext_b-1] + end + train.path[train.path_ext_b] = adj_pos + train.path_dist[train.path_ext_b] = vector.distance(pos, adj_pos) + end + + return train.path[index], (index<=train.path_trk_f and index>=train.path_trk_b) + +end + +-- interpolated position to fractional index given, and angle based on path_dir +-- returns: pos, angle(yaw) +function advtrains.path_get_interpolated(train, index) + local i_floor = atfloor(index) + local i_ceil = i_floor + 1 + local frac = index - i_floor + local p_floor, = advtrains.path_get(train, i_floor) + local p_ceil = advtrains.path_get(train, i_ceil) + + local d_floor = train.path_dir[i_floor] + local d_ceil = train.path_dir[i_ceil] + local a_floor = advtrains.dir_to_angle(d_floor) + local a_ceil = advtrains.dir_to_angle(d_ceil) + + local ang = advtrains.minAngleDiffRad(a_floor, a_ceil) + + return vector.add(p_floor, vector.multiply(vector.subtract(p_ceil, p_floor), frac), (a_floor + frac * ang)%(2*math.pi) -- TODO does this behave correctly? +end + +function advtrains.path_get_by_offset(train, index, offset) + local pos_in_train_left=pit + local index=train.index + if pos_in_train_left>(index-math.floor(index))*(train.path_dist[math.floor(index)] or 1) then + pos_in_train_left=pos_in_train_left - (index-math.floor(index))*(train.path_dist[math.floor(index)] or 1) + index=math.floor(index) + while pos_in_train_left>(train.path_dist[index-1] or 1) do + pos_in_train_left=pos_in_train_left - (train.path_dist[index-1] or 1) + index=index-1 + end + index=index-(pos_in_train_left/(train.path_dist[index-1] or 1)) + else + index=index-(pos_in_train_left/(train.path_dist[math.floor(index-1)] or 1)) + end + return index +end diff --git a/advtrains/trainlogic.lua b/advtrains/trainlogic.lua index 449d624..181446c 100644 --- a/advtrains/trainlogic.lua +++ b/advtrains/trainlogic.lua @@ -219,7 +219,7 @@ function advtrains.train_step_a(id, train, dtime) - The next step, mistake is recognized, train leaves some positions. From there, everything works again. To overcome this, we will generate the full required path here so that path_dist is available for get_train_end_index(). ]] - advtrains.pathpredict(id, train) + advtrains.pathpredict(id, train, 3, -train.trainlen-3) end --- 2a. set train.end_index which is required in different places, IF IT IS NOT SET YET by STMT afterwards. --- @@ -359,9 +359,36 @@ function advtrains.train_step_a(id, train, dtime) --- 4a. update train.end_index to the new position --- train.end_index=advtrains.get_train_end_index(train) + --- 4b calculate how far a path is required --- + local path_req_dd = 10 -- path required in driving direction + local path_req_ndd = 4 -- path required against driving direction + + -- when using easyBSS (block signalling), we need to make sure that the whole brake distance is known + if advtrains_easybss then + local acc_all = t_accel_all[1] + local acc_eng = t_accel_eng[1] + local nwagons = #train.trainparts + local acc = acc_all + (acc_eng*train.locomotives_in_train)/nwagons + local vel = train.velocity + local brakedst = (vel*vel) / (2*acc) + path_req_dd = math.ceil(brakedst+10) + end + + + local idx_front=math.max(train.index, train.detector_old_index) + local idx_back=math.min(train.end_index, train.detector_old_end_index) + local path_req_front, path_req_back + if train.movedir == 1 then + path_req_front = atround(idx_front + path_req_dd) + path_req_back = atround(idx_back - path_req_ndd) + else + path_req_front = atround(idx_front + path_req_ndd) + path_req_back = atround(idx_back - path_req_dd) + end + --- 5. extend path as necessary --- --why this is an extra function, see under 3. - advtrains.pathpredict(id, train, true) + advtrains.pathpredict(id, train, path_req_front, path_req_back) --- 5a. make pos/yaw available for possible recover calls --- if train.max_index_on_track