
commit
2ff8f5fd46
7 changed files with 1607 additions and 0 deletions
@ -0,0 +1,431 @@
|
||||
--advtrains by orwell96, see readme.txt |
||||
|
||||
local dir_trans_tbl={ |
||||
[0]={x=0, z=1, y=0}, |
||||
[1]={x=1, z=2, y=0}, |
||||
[2]={x=1, z=1, y=0}, |
||||
[3]={x=2, z=1, y=0}, |
||||
[4]={x=1, z=0, y=0}, |
||||
[5]={x=2, z=-1, y=0}, |
||||
[6]={x=1, z=-1, y=0}, |
||||
[7]={x=1, z=-2, y=0}, |
||||
[8]={x=0, z=-1, y=0}, |
||||
[9]={x=-1, z=-2, y=0}, |
||||
[10]={x=-1, z=-1, y=0}, |
||||
[11]={x=-2, z=-1, y=0}, |
||||
[12]={x=-1, z=0, y=0}, |
||||
[13]={x=-2, z=1, y=0}, |
||||
[14]={x=-1, z=1, y=0}, |
||||
[15]={x=-1, z=2, y=0}, |
||||
} |
||||
|
||||
local dir_angle_tbl={} |
||||
for d,v in pairs(dir_trans_tbl) do |
||||
local uvec = vector.normalize(v) |
||||
dir_angle_tbl[d] = math.atan2(-uvec.x, uvec.z) |
||||
end |
||||
|
||||
|
||||
function advtrains.dir_to_angle(dir) |
||||
return dir_angle_tbl[dir] or error("advtrains: in helpers.lua/dir_to_angle() given dir="..(dir or "nil")) |
||||
end |
||||
|
||||
function advtrains.dirCoordSet(coord, dir) |
||||
return vector.add(coord, advtrains.dirToCoord(dir)) |
||||
end |
||||
advtrains.pos_add_dir = advtrains.dirCoordSet |
||||
|
||||
function advtrains.pos_add_angle(pos, ang) |
||||
-- 0 is +Z -> meaning of sin/cos swapped |
||||
return vector.add(pos, {x = -math.sin(ang), y = 0, z = math.cos(ang)}) |
||||
end |
||||
|
||||
function advtrains.dirToCoord(dir) |
||||
return dir_trans_tbl[dir] or error("advtrains: in helpers.lua/dir_to_vector() given dir="..(dir or "nil")) |
||||
end |
||||
advtrains.dir_to_vector = advtrains.dirToCoord |
||||
|
||||
function advtrains.maxN(list, expectstart) |
||||
local n=expectstart or 0 |
||||
while list[n] do |
||||
n=n+1 |
||||
end |
||||
return n-1 |
||||
end |
||||
|
||||
function advtrains.minN(list, expectstart) |
||||
local n=expectstart or 0 |
||||
while list[n] do |
||||
n=n-1 |
||||
end |
||||
return n+1 |
||||
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)} |
||||
end |
||||
|
||||
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 = advtrains.dir_to_angle(conn1) |
||||
local yaw2 = advtrains.dir_to_angle(conn2) |
||||
local adiff1 = advtrains.minAngleDiffRad(yaw, yaw1) |
||||
local adiff2 = advtrains.minAngleDiffRad(yaw, yaw2) |
||||
|
||||
if math.abs(adiff2)<math.abs(adiff1) then |
||||
return conn2 |
||||
else |
||||
return conn1 |
||||
end |
||||
end |
||||
|
||||
function advtrains.yawToAnyDir(yaw) |
||||
local min_conn, min_diff=0, 10 |
||||
for conn, vec in pairs(advtrains.dir_trans_tbl) do |
||||
local yaw1 = advtrains.dir_to_angle(conn) |
||||
local diff = math.abs(advtrains.minAngleDiffRad(yaw, yaw1)) |
||||
if diff < min_diff then |
||||
min_conn = conn |
||||
min_diff = diff |
||||
end |
||||
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 yaw1 = advtrains.dir_to_angle(conn.c) |
||||
local diff = math.abs(advtrains.minAngleDiffRad(yaw, yaw1)) |
||||
if diff < min_diff then |
||||
min_connid = connid |
||||
min_diff = diff |
||||
end |
||||
end |
||||
return min_connid |
||||
end |
||||
|
||||
local pi, pi2 = math.pi, 2*math.pi |
||||
function advtrains.minAngleDiffRad(r1, r2) |
||||
while r1>pi2 do |
||||
r1=r1-pi2 |
||||
end |
||||
while r1<0 do |
||||
r1=r1+pi2 |
||||
end |
||||
while r2>pi2 do |
||||
r2=r2-pi2 |
||||
end |
||||
while r1<0 do |
||||
r2=r2+pi2 |
||||
end |
||||
local try1=r2-r1 |
||||
local try2=r2+pi2-r1 |
||||
local try3=r2-pi2-r1 |
||||
|
||||
local minabs = math.min(math.abs(try1), math.abs(try2), math.abs(try3)) |
||||
if minabs==math.abs(try1) then |
||||
return try1 |
||||
end |
||||
if minabs==math.abs(try2) then |
||||
return try2 |
||||
end |
||||
if minabs==math.abs(try3) then |
||||
return try3 |
||||
end |
||||
end |
||||
|
||||
|
||||
-- Takes 2 connections (0...AT_CMAX) as argument |
||||
-- Returns the angle median of those 2 positions from the pov |
||||
-- of standing on the cdir1 side and looking towards cdir2 |
||||
-- cdir1 - >NODE> - cdir2 |
||||
function advtrains.conn_angle_median(cdir1, cdir2) |
||||
local ang1 = advtrains.dir_to_angle(advtrains.oppd(cdir1)) |
||||
local ang2 = advtrains.dir_to_angle(cdir2) |
||||
return ang1 + advtrains.minAngleDiffRad(ang1, ang2)/2 |
||||
end |
||||
|
||||
function advtrains.merge_tables(a, ...) |
||||
local new={} |
||||
for _,t in ipairs({a,...}) do |
||||
for k,v in pairs(t) do new[k]=v end |
||||
end |
||||
return new |
||||
end |
||||
function advtrains.save_keys(tbl, keys) |
||||
local new={} |
||||
for _,key in ipairs(keys) do |
||||
new[key] = tbl[key] |
||||
end |
||||
return new |
||||
end |
||||
|
||||
function advtrains.get_real_index_position(path, index) |
||||
if not path or not index then return end |
||||
|
||||
local first_pos=path[math.floor(index)] |
||||
local second_pos=path[math.floor(index)+1] |
||||
|
||||
if not first_pos or not second_pos then return nil end |
||||
|
||||
local factor=index-math.floor(index) |
||||
local actual_pos={x=first_pos.x-(first_pos.x-second_pos.x)*factor, y=first_pos.y-(first_pos.y-second_pos.y)*factor, z=first_pos.z-(first_pos.z-second_pos.z)*factor,} |
||||
return actual_pos |
||||
end |
||||
function advtrains.pos_median(pos1, pos2) |
||||
return {x=pos1.x-(pos1.x-pos2.x)*0.5, y=pos1.y-(pos1.y-pos2.y)*0.5, z=pos1.z-(pos1.z-pos2.z)*0.5} |
||||
end |
||||
function advtrains.abs_ceil(i) |
||||
return math.ceil(math.abs(i))*math.sign(i) |
||||
end |
||||
|
||||
function advtrains.serialize_inventory(inv) |
||||
local ser={} |
||||
local liszts=inv:get_lists() |
||||
for lisztname, liszt in pairs(liszts) do |
||||
ser[lisztname]={} |
||||
for idx, item in ipairs(liszt) do |
||||
local istring=item:to_string() |
||||
if istring~="" then |
||||
ser[lisztname][idx]=istring |
||||
end |
||||
end |
||||
end |
||||
return minetest.serialize(ser) |
||||
end |
||||
function advtrains.deserialize_inventory(sers, inv) |
||||
local ser=minetest.deserialize(sers) |
||||
if ser then |
||||
inv:set_lists(ser) |
||||
return true |
||||
end |
||||
return false |
||||
end |
||||
|
||||
--is_protected wrapper that checks for protection_bypass privilege |
||||
function advtrains.is_protected(pos, name) |
||||
if not name then |
||||
error("advtrains.is_protected() called without name parameter!") |
||||
end |
||||
if minetest.check_player_privs(name, {protection_bypass=true}) then |
||||
--player can bypass protection |
||||
return false |
||||
end |
||||
return minetest.is_protected(pos, name) |
||||
end |
||||
|
||||
function advtrains.is_creative(name) |
||||
if not name then |
||||
error("advtrains.is_creative() called without name parameter!") |
||||
end |
||||
if minetest.check_player_privs(name, {creative=true}) then |
||||
return true |
||||
end |
||||
return minetest.settings:get_bool("creative_mode") |
||||
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 |
||||
|
||||
|
||||
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 <connid of other rail> <connid of this rail> |
||||
end |
||||
return false |
||||
end |
||||
|
||||
|
||||
-- returns: <adjacent pos>, <conn index of adjacent>, <my conn index>, <railheight of adjacent> |
||||
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, nco = advtrains.get_adjacent_rail(this_pos, this_conns, coni) |
||||
if adj_pos then return adj_pos,adj_conn_idx,coni,nry, nco 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, nextconns |
||||
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 |
||||
|
||||
function advtrains.random_id() |
||||
local idst="" |
||||
for i=0,5 do |
||||
idst=idst..(math.random(0,9)) |
||||
end |
||||
return idst |
||||
end |
||||
-- Shorthand for pos_to_string and round_vector_floor_y |
||||
function advtrains.roundfloorpts(pos) |
||||
return minetest.pos_to_string(advtrains.round_vector_floor_y(pos)) |
||||
end |
||||
|
||||
-- insert an element into a table if it does not yet exist there |
||||
-- equalfunc is a function to compare equality, defaults to == |
||||
-- returns true if the element was inserted |
||||
function advtrains.insert_once(tab, elem, equalfunc) |
||||
for _,e in pairs(tab) do |
||||
if equalfunc and equalfunc(elem, e) or e==elem then return false end |
||||
end |
||||
tab[#tab+1] = elem |
||||
return true |
||||
end |
||||
|
||||
local hext = { [0]="0",[1]="1",[2]="2",[3]="3",[4]="4",[5]="5",[6]="6",[7]="7",[8]="8",[9]="9",[10]="A",[11]="B",[12]="C",[13]="D",[14]="E",[15]="F"} |
||||
local dect = { ["0"]=0,["1"]=1,["2"]=2,["3"]=3,["4"]=4,["5"]=5,["6"]=6,["7"]=7,["8"]=8,["9"]=9,["A"]=10,["B"]=11,["C"]=12,["D"]=13,["E"]=14,["F"]=15} |
||||
|
||||
local f = atfloor |
||||
|
||||
local function hex(i) |
||||
local x=i+32768 |
||||
local c4 = x % 16 |
||||
x = f(x / 16) |
||||
local c3 = x % 16 |
||||
x = f(x / 16) |
||||
local c2 = x % 16 |
||||
x = f(x / 16) |
||||
local c1 = x % 16 |
||||
return (hext[c1]) .. (hext[c2]) .. (hext[c3]) .. (hext[c4]) |
||||
end |
||||
|
||||
local function c(s,i) return dect[string.sub(s,i,i)] end |
||||
|
||||
local function dec(s) |
||||
return (c(s,1)*4096 + c(s,2)*256 + c(s,3)*16 + c(s,4))-32768 |
||||
end |
||||
-- Takes a position vector and outputs a encoded value suitable as table index |
||||
-- This is essentially a hexadecimal representation of the position (+32768) |
||||
-- Order (YYY)YXXXXZZZZ |
||||
function advtrains.encode_pos(pos) |
||||
return hex(pos.y) .. hex(pos.x) .. hex(pos.z) |
||||
end |
||||
|
||||
-- decodes a position encoded with encode_pos |
||||
function advtrains.decode_pos(pts) |
||||
local stry = string.sub(pts, 1,4) |
||||
local strx = string.sub(pts, 5,8) |
||||
local strz = string.sub(pts, 9,12) |
||||
return vector.new(dec(strx), dec(stry), dec(strz)) |
||||
end |
||||
|
||||
--[[ Benchmarking code |
||||
local tdt = {} |
||||
local tlt = {} |
||||
local tet = {} |
||||
|
||||
for i=1,1000000 do |
||||
tdt[i] = vector.new(math.random(-65536, 65535), math.random(-65536, 65535), math.random(-65536, 65535)) |
||||
if i%1000 == 0 then |
||||
tlt[#tlt+1] = tdt[i] |
||||
end |
||||
end |
||||
|
||||
local t1=os.clock() |
||||
for i=1,1000000 do |
||||
local pe = advtrains.encode_pos(tdt[i]) |
||||
local pb = advtrains.decode_pos(pe) |
||||
tet[pe] = i |
||||
end |
||||
for i,v in ipairs(tlt) do |
||||
local lk = tet[advtrains.encode_pos(v)] |
||||
end |
||||
atdebug("endec",os.clock()-t1,"s") |
||||
|
||||
tet = {} |
||||
|
||||
t1=os.clock() |
||||
for i=1,1000000 do |
||||
local pe = minetest.pos_to_string(tdt[i]) |
||||
local pb = minetest.string_to_pos(pe) |
||||
tet[pe] = i |
||||
end |
||||
for i,v in ipairs(tlt) do |
||||
local lk = tet[minetest.pos_to_string(v)] |
||||
end |
||||
atdebug("pts",os.clock()-t1,"s") |
||||
|
||||
--Results: |
||||
--2018-11-29 16:57:08: ACTION[Main]: [advtrains]endec 1.786451 s |
||||
--2018-11-29 16:57:10: ACTION[Main]: [advtrains]pts 2.566377 s |
||||
]] |
||||
|
||||
|
@ -0,0 +1,230 @@
|
||||
-- advtrains track map generator |
||||
-- Usage:... |
||||
|
||||
-- Viewport maximum coordinate in all directions |
||||
local maxc = 5000 |
||||
|
||||
-- embed an image called "world.png" |
||||
local wimg = false |
||||
-- image file resolution (not world resolution!) |
||||
local wimresx = 3000 |
||||
local wimresy = 3000 |
||||
-- one pixel is ... nodes |
||||
local wimscale = 4 |
||||
|
||||
|
||||
|
||||
--Constant for maximum connection value/division of the circle |
||||
AT_CMAX = 16 |
||||
|
||||
advtrains = {} |
||||
minetest = {} |
||||
core = minetest |
||||
|
||||
--table for track nodes/connections |
||||
trackconns = {} |
||||
|
||||
-- math library seems to be missing this function |
||||
math.hypot = function(a,b) return math.sqrt(a*a + b*b) end |
||||
|
||||
-- need to declare this for trackdefs |
||||
function attrans(str) return str end |
||||
|
||||
-- pos to string |
||||
local function pts(pos) |
||||
return pos.x .. "," .. pos.y .. "," .. pos.z |
||||
end |
||||
|
||||
--Advtrains dump (special treatment of pos and sigd) |
||||
function atdump(t, intend) |
||||
local str |
||||
if type(t)=="table" then |
||||
if t.x and t.y and t.z then |
||||
str=minetest.pos_to_string(t) |
||||
elseif t.p and t.s then -- interlocking sigd |
||||
str="S["..minetest.pos_to_string(t.p).."/"..t.s.."]" |
||||
else |
||||
str="{" |
||||
local intd = (intend or "") .. " " |
||||
for k,v in pairs(t) do |
||||
if type(k)~="string" or not string.match(k, "^path[_]?") then |
||||
-- do not print anything path-related |
||||
str = str .. "\n" .. intd .. atdump(k, intd) .. " = " ..atdump(v, intd) |
||||
end |
||||
end |
||||
str = str .. "\n" .. (intend or "") .. "}" |
||||
end |
||||
elseif type(t)=="boolean" then |
||||
if t then |
||||
str="true" |
||||
else |
||||
str="false" |
||||
end |
||||
elseif type(t)=="function" then |
||||
str="<function>" |
||||
elseif type(t)=="userdata" then |
||||
str="<userdata>" |
||||
else |
||||
str=""..t |
||||
end |
||||
return str |
||||
end |
||||
|
||||
dofile("vector.lua") |
||||
dofile("serialize.lua") |
||||
dofile("helpers.lua") |
||||
dofile("tracks.lua") |
||||
dofile("track_defs.lua") |
||||
|
||||
dofile("nodedb.lua") |
||||
|
||||
|
||||
-- Load saves |
||||
local file, err = io.open("advtrains", "r") |
||||
local tbl = minetest.deserialize(file:read("*a")) |
||||
if type(tbl) ~= "table" then |
||||
error("not a table") |
||||
end |
||||
if tbl.version then |
||||
|
||||
advtrains.ndb.load_data(tbl.ndb) |
||||
|
||||
else |
||||
error("Incompatible save format!") |
||||
end |
||||
file:close() |
||||
|
||||
-- open svg file |
||||
|
||||
local svgfile = io.open("out.svg", "w") |
||||
|
||||
svgfile:write([[ |
||||
<?xml version="1.0" standalone="no" ?> |
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" |
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> |
||||
<svg width="1024" height="800" xmlns="http://www.w3.org/2000/svg" |
||||
xmlns:xlink="http://www.w3.org/1999/xlink" ]]) |
||||
|
||||
|
||||
svgfile:write('viewBox="'..(-maxc)..' '..(-maxc)..' '..(2*maxc)..' '..(2*maxc)..'" >') |
||||
|
||||
|
||||
svgfile:write([[ |
||||
<circle cx="0" cy="0" r="2" stroke="red" stroke-width="1" /> |
||||
]]) |
||||
|
||||
if wimg then |
||||
local wimx = -(wimresx*wimscale/2) |
||||
local wimy = -(wimresy*wimscale/2) |
||||
local wimw = wimresx*wimscale |
||||
local wimh = wimresy*wimscale |
||||
|
||||
svgfile:write('<image xlink:href="world.png" x="'..wimx..'" y="'..wimy..'" height="'..wimh..'px" width="'..wimw..'px"/>') |
||||
end |
||||
|
||||
local function writec(text) |
||||
print(text) |
||||
svgfile:write("<!-- " .. text .. " -->\n") |
||||
end |
||||
|
||||
|
||||
-- everything set up. Start generating an SVG |
||||
-- All nodes that have been fit into a polyline are removed from the NDB, in order to not draw them again. |
||||
|
||||
-- "Restart points" for the breadth-first traverser (set when more than 2 conns present) |
||||
-- {pos = <position>, connid = <int>, conn = <conndef>} |
||||
-- Note that the node at "pos" is already deleted from the NDB at the time of recall, therefore "conn" is specified |
||||
local bfs_rsp = {} |
||||
|
||||
|
||||
-- Points of the current polyline. Inserted as xyz vectors, maybe we'll use the y value one day |
||||
local current_polyline = {} |
||||
|
||||
-- Traverser function from interlocking, highly modified |
||||
local function gen_rsp_polyline(rsp) |
||||
|
||||
-- trick a bit |
||||
local pos, connid, conns = rsp.pos, 1, {rsp.conn} |
||||
current_polyline[#current_polyline+1] = pos |
||||
|
||||
while true do |
||||
local adj_pos, adj_connid, conn_idx, nextrail_y, next_conns = advtrains.get_adjacent_rail(pos, conns, connid) |
||||
if not adj_pos then |
||||
return |
||||
end |
||||
-- continue traversing |
||||
local conn_mainbranch |
||||
for nconnid, nconn in ipairs(next_conns) do |
||||
if adj_connid ~= nconnid then |
||||
if not conn_mainbranch then |
||||
--use the first one found to continue |
||||
conn_mainbranch = nconnid |
||||
--writec(nconnid.." nconn mainbranch") |
||||
else |
||||
-- insert bfs reminders for other conns |
||||
table.insert(bfs_rsp, {pos = adj_pos, connid = nconnid, conn = nconn}) |
||||
--writec(nconnid.." nconn bfs") |
||||
end |
||||
end |
||||
end |
||||
|
||||
-- save in polyline and delete from ndb |
||||
--writec("Saved pos: "..pts(adj_pos).." mainbranch cont "..conn_mainbranch.." nextconns "..atdump(next_conns)) |
||||
current_polyline[#current_polyline+1] = adj_pos |
||||
advtrains.ndb.clear(adj_pos) |
||||
|
||||
pos, connid, conns = adj_pos, conn_mainbranch, next_conns |
||||
|
||||
end |
||||
end |
||||
|
||||
|
||||
local function polyline_write(pl) |
||||
local str = {'<polyline style="fill:none;stroke:black;stroke-width:1" points="'} |
||||
|
||||
local i |
||||
local e |
||||
local lastldir = {x=0, y=0} |
||||
for i=1,#pl do |
||||
e = pl[i] |
||||
-- Note that we mirror y, so positive z is up |
||||
table.insert(str, e.x .. "," .. -(e.z) .. " ") |
||||
end |
||||
|
||||
table.insert(str, '" />\n') |
||||
|
||||
svgfile:write(table.concat(str)) |
||||
end |
||||
|
||||
|
||||
-- while there are entries in the nodedb |
||||
-- 1. find a starting point |
||||
local stpos, conns = advtrains.ndb.mapper_find_starting_point() |
||||
while stpos do |
||||
|
||||
writec("Restart at position "..pts(stpos)) |
||||
for connid, conn in ipairs(conns) do |
||||
table.insert(bfs_rsp, {pos = stpos, connid = connid, conn = conn}) |
||||
end |
||||
advtrains.ndb.clear(stpos) |
||||
|
||||
-- 2. while there are BFS entries |
||||
while #bfs_rsp > 0 do |
||||
-- make polylines |
||||
local current_rsp = bfs_rsp[#bfs_rsp] |
||||
bfs_rsp[#bfs_rsp] = nil |
||||
--print("Starting polyline at "..pts(current_rsp.pos).."/"..current_rsp.connid) |
||||
|
||||
|
||||
current_polyline = {} |
||||
|
||||
gen_rsp_polyline(current_rsp) |
||||
|
||||
polyline_write(current_polyline) |
||||
end |
||||
stpos, conns = advtrains.ndb.mapper_find_starting_point() |
||||
end |
||||
|
||||
svgfile:write("</svg>") |
||||
svgfile:close() |
||||
|
@ -0,0 +1,163 @@
|
||||
--nodedb.lua |
||||
--database of all nodes that have 'save_in_at_nodedb' field set to true in node definition |
||||
|
||||
|
||||
--serialization format: |
||||
--(2byte z) (2byte y) (2byte x) (2byte contentid) |
||||
--contentid := (14bit nodeid, 2bit param2) |
||||
|
||||
local function int_to_bytes(i) |
||||
local x=i+32768--clip to positive integers |
||||
local cH = math.floor(x / 256) % 256; |
||||
local cL = math.floor(x ) % 256; |
||||
return(string.char(cH, cL)); |
||||
end |
||||
local function bytes_to_int(bytes) |
||||
local t={string.byte(bytes,1,-1)} |
||||
local n = |
||||
t[1] * 256 + |
||||
t[2] |
||||
return n-32768 |
||||
end |
||||
local function l2b(x) |
||||
return x%4 |
||||
end |
||||
local function u14b(x) |
||||
return math.floor(x/4) |
||||
end |
||||
local ndb={} |
||||
|
||||
--local variables for performance |
||||
local ndb_nodeids={} |
||||
local ndb_nodes={} |
||||
|
||||
local function ndbget(x,y,z) |
||||
local ny=ndb_nodes[y] |
||||
if ny then |
||||
local nx=ny[x] |
||||
if nx then |
||||
return nx[z] |
||||
end |
||||
end |
||||
return nil |
||||
end |
||||
local function ndbset(x,y,z,v) |
||||
if not ndb_nodes[y] then |
||||
ndb_nodes[y]={} |
||||
end |
||||
if not ndb_nodes[y][x] then |
||||
ndb_nodes[y][x]={} |
||||
end |
||||
ndb_nodes[y][x][z]=v |
||||
end |
||||
|
||||
|
||||
local path="advtrains_ndb2" |
||||
--load |
||||
--nodeids get loaded by advtrains init.lua and passed here |
||||
function ndb.load_data(data) |
||||
ndb_nodeids = data and data.nodeids or {} |
||||
local file, err = io.open(path, "rb") |
||||
if not file then |
||||
print("Couldn't load the node database: ", err or "Unknown Error") |
||||
else |
||||
local cnt=0 |
||||
local hst_z=file:read(2) |
||||
local hst_y=file:read(2) |
||||
local hst_x=file:read(2) |
||||
local cid=file:read(2) |
||||
while hst_z and hst_y and hst_x and cid and #hst_z==2 and #hst_y==2 and #hst_x==2 and #cid==2 do |
||||
ndbset(bytes_to_int(hst_x), bytes_to_int(hst_y), bytes_to_int(hst_z), bytes_to_int(cid)) |
||||
cnt=cnt+1 |
||||
hst_z=file:read(2) |
||||
hst_y=file:read(2) |
||||
hst_x=file:read(2) |
||||
cid=file:read(2) |
||||
end |
||||
print("nodedb: read", cnt, "nodes.") |
||||
file:close() |
||||
end |
||||
end |
||||
|
||||
--function to get node. track database is not helpful here. |
||||
function ndb.get_node_or_nil(pos) |
||||
-- FIX for bug found on linuxworks server: |
||||
-- a loaded node might get read before the LBM has updated its state, resulting in wrongly set signals and switches |
||||
-- -> Using the saved node prioritarily. |
||||
local node = ndb.get_node_raw(pos) |
||||
if node then |
||||
return node |
||||
else |
||||
-- no minetest here |
||||
return nil |
||||
end |
||||
end |
||||
function ndb.get_node(pos) |
||||
local n=ndb.get_node_or_nil(pos) |
||||
if not n then |
||||
return {name="ignore", param2=0} |
||||
end |
||||
return n |
||||
end |
||||
function ndb.get_node_raw(pos) |
||||
local cid=ndbget(pos.x, pos.y, pos.z) |
||||
if cid then |
||||
local nodeid = ndb_nodeids[u14b(cid)] |
||||
if nodeid then |
||||
return {name=nodeid, param2 = l2b(cid)} |
||||
end |
||||
end |
||||
return nil |
||||
end |
||||
|
||||
function ndb.clear(pos) |
||||
ndbset(pos.x, pos.y, pos.z, nil) |
||||
end |
||||
|
||||
|
||||
--get_node with pseudoload. now we only need track data, so we can use the trackdb as second fallback |
||||
--nothing new will be saved inside the trackdb. |
||||
--returns: |
||||
--true, conn1, conn2, rely1, rely2, railheight in case everything's right. |
||||
--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) |
||||
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 |
||||
|
||||
local conns, railheight, tracktype=advtrains.get_track_connections(node.name, node.param2) |
||||
|
||||
if not conns then |
||||
return false |
||||
end |
||||
|
||||
return true, conns, railheight |
||||
end |
||||
|
||||
|
||||
-- mapper-specific |
||||
|
||||
function ndb.mapper_find_starting_point() |
||||
for y, ty in pairs(ndb_nodes) do |
||||
for x, tx in pairs(ty) do |
||||
for z, v in pairs(tx) do |
||||
local pos = {x=x, y=y, z=z} |
||||
local node_ok, conns, _ = advtrains.get_rail_info_at(pos) |
||||
if node_ok then |
||||
return pos, conns |
||||
else |
||||
-- this is a signal or something similar, ignore. |
||||
tx[z]=nil |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
||||
|
||||
|
||||
advtrains.ndb = ndb |
@ -0,0 +1,152 @@
|
||||
--== Insert track defs here! ==-- |
||||
-- Default tracks for advtrains |
||||
-- (c) orwell96 and contributors |
||||
|
||||
--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={}, |
||||
}, advtrains.ap.t_30deg_flat) |
||||
--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) |
||||
|
||||
--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) |
||||
|
||||
|
||||
|
||||
-- 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) |
||||
|
||||
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, |
||||
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, |
||||
|
||||
advtrains = { |
||||
on_train_enter = function(pos, train_id) |
||||
train_load(pos, train_id, false) |
||||
end, |
||||
}, |
||||
} |
||||
end |
||||
}, advtrains.trackpresets.t_30deg_straightonly) |
||||
|
||||
|
||||
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_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}) |
||||
mesecon.receptor_on(pos, advtrains.meseconrules) |
||||
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_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}) |
||||
mesecon.receptor_off(pos, advtrains.meseconrules) |
||||
end |
||||
} |
||||
} |
||||
end |
||||
}, advtrains.ap.t_30deg_straightonly_noplacer) |
||||
|
||||
|
||||
--== END insert track defs ==-- |
@ -0,0 +1,265 @@
|
||||
--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 = "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, |
||||
tpdefault="st", |
||||
trackworker={ |
||||
["swrcr"]="st", |
||||
["swrst"]="st", |
||||
["cr"]="swlst", |
||||
["swlcr"]="swrcr", |
||||
["swlst"]="swrst", |
||||
}, |
||||
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 = "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, |
||||
tpdefault="st", |
||||
trackworker={ |
||||
["swrcr"]="st", |
||||
["swrst"]="st", |
||||
["cr"]="swlst", |
||||
["swlcr"]="swrcr", |
||||
["swlst"]="swrst", |
||||
}, |
||||
rotation={"", "_30", "_45", "_60"}, |
||||
} |
||||
advtrains.trackpresets = advtrains.ap |
||||
|
||||
--definition format: ([] optional) |
||||
--[[{ |
||||
nodename_prefix |
||||
texture_prefix |
||||
[shared_texture] |
||||
models_prefix |
||||