Initial version
This commit is contained in:
commit
2ff8f5fd46
431
helpers.lua
Normal file
431
helpers.lua
Normal file
@ -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
|
||||||
|
]]
|
||||||
|
|
||||||
|
|
230
main.lua
Normal file
230
main.lua
Normal file
@ -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()
|
||||||
|
|
163
nodedb.lua
Normal file
163
nodedb.lua
Normal file
@ -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
|
221
serialize.lua
Executable file
221
serialize.lua
Executable file
@ -0,0 +1,221 @@
|
|||||||
|
--- Lua module to serialize values as Lua code.
|
||||||
|
-- From: https://github.com/fab13n/metalua/blob/no-dll/src/lib/serialize.lua
|
||||||
|
-- License: MIT
|
||||||
|
-- @copyright 2006-2997 Fabien Fleutot <metalua@gmail.com>
|
||||||
|
-- @author Fabien Fleutot <metalua@gmail.com>
|
||||||
|
-- @author ShadowNinja <shadowninja@minetest.net>
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Serialize an object into a source code string. This string, when passed as
|
||||||
|
-- an argument to deserialize(), returns an object structurally identical to
|
||||||
|
-- the original one. The following are currently supported:
|
||||||
|
-- * Booleans, numbers, strings, and nil.
|
||||||
|
-- * Functions; uses interpreter-dependent (and sometimes platform-dependent) bytecode!
|
||||||
|
-- * Tables; they can cantain multiple references and can be recursive, but metatables aren't saved.
|
||||||
|
-- This works in two phases:
|
||||||
|
-- 1. Recursively find and record multiple references and recursion.
|
||||||
|
-- 2. Recursively dump the value into a string.
|
||||||
|
-- @param x Value to serialize (nil is allowed).
|
||||||
|
-- @return load()able string containing the value.
|
||||||
|
function core.serialize(x)
|
||||||
|
local local_index = 1 -- Top index of the "_" local table in the dump
|
||||||
|
-- table->nil/1/2 set of tables seen.
|
||||||
|
-- nil = not seen, 1 = seen once, 2 = seen multiple times.
|
||||||
|
local seen = {}
|
||||||
|
|
||||||
|
-- nest_points are places where a table appears within itself, directly
|
||||||
|
-- or not. For instance, all of these chunks create nest points in
|
||||||
|
-- table x: "x = {}; x[x] = 1", "x = {}; x[1] = x",
|
||||||
|
-- "x = {}; x[1] = {y = {x}}".
|
||||||
|
-- To handle those, two tables are used by mark_nest_point:
|
||||||
|
-- * nested - Transient set of tables being currently traversed.
|
||||||
|
-- Used for detecting nested tables.
|
||||||
|
-- * nest_points - parent->{key=value, ...} table cantaining the nested
|
||||||
|
-- keys and values in the parent. They're all dumped after all the
|
||||||
|
-- other table operations have been performed.
|
||||||
|
--
|
||||||
|
-- mark_nest_point(p, k, v) fills nest_points with information required
|
||||||
|
-- to remember that key/value (k, v) creates a nest point in table
|
||||||
|
-- parent. It also marks "parent" and the nested item(s) as occuring
|
||||||
|
-- multiple times, since several references to it will be required in
|
||||||
|
-- order to patch the nest points.
|
||||||
|
local nest_points = {}
|
||||||
|
local nested = {}
|
||||||
|
local function mark_nest_point(parent, k, v)
|
||||||
|
local nk, nv = nested[k], nested[v]
|
||||||
|
local np = nest_points[parent]
|
||||||
|
if not np then
|
||||||
|
np = {}
|
||||||
|
nest_points[parent] = np
|
||||||
|
end
|
||||||
|
np[k] = v
|
||||||
|
seen[parent] = 2
|
||||||
|
if nk then seen[k] = 2 end
|
||||||
|
if nv then seen[v] = 2 end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- First phase, list the tables and functions which appear more than
|
||||||
|
-- once in x.
|
||||||
|
local function mark_multiple_occurences(x)
|
||||||
|
local tp = type(x)
|
||||||
|
if tp ~= "table" and tp ~= "function" then
|
||||||
|
-- No identity (comparison is done by value, not by instance)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if seen[x] == 1 then
|
||||||
|
seen[x] = 2
|
||||||
|
elseif seen[x] ~= 2 then
|
||||||
|
seen[x] = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if tp == "table" then
|
||||||
|
nested[x] = true
|
||||||
|
for k, v in pairs(x) do
|
||||||
|
if nested[k] or nested[v] then
|
||||||
|
mark_nest_point(x, k, v)
|
||||||
|
else
|
||||||
|
mark_multiple_occurences(k)
|
||||||
|
mark_multiple_occurences(v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
nested[x] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local dumped = {} -- object->varname set
|
||||||
|
local local_defs = {} -- Dumped local definitions as source code lines
|
||||||
|
|
||||||
|
-- Mutually recursive local functions:
|
||||||
|
local dump_val, dump_or_ref_val
|
||||||
|
|
||||||
|
-- If x occurs multiple times, dump the local variable rather than
|
||||||
|
-- the value. If it's the first time it's dumped, also dump the
|
||||||
|
-- content in local_defs.
|
||||||
|
function dump_or_ref_val(x)
|
||||||
|
if seen[x] ~= 2 then
|
||||||
|
return dump_val(x)
|
||||||
|
end
|
||||||
|
local var = dumped[x]
|
||||||
|
if var then -- Already referenced
|
||||||
|
return var
|
||||||
|
end
|
||||||
|
-- First occurence, create and register reference
|
||||||
|
local val = dump_val(x)
|
||||||
|
local i = local_index
|
||||||
|
local_index = local_index + 1
|
||||||
|
var = "_["..i.."]"
|
||||||
|
local_defs[#local_defs + 1] = var.." = "..val
|
||||||
|
dumped[x] = var
|
||||||
|
return var
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Second phase. Dump the object; subparts occuring multiple times
|
||||||
|
-- are dumped in local variables which can be referenced multiple
|
||||||
|
-- times. Care is taken to dump local vars in a sensible order.
|
||||||
|
function dump_val(x)
|
||||||
|
local tp = type(x)
|
||||||
|
if x == nil then return "nil"
|
||||||
|
elseif tp == "string" then return string.format("%q", x)
|
||||||
|
elseif tp == "boolean" then return x and "true" or "false"
|
||||||
|
elseif tp == "function" then
|
||||||
|
return string.format("loadstring(%q)", string.dump(x))
|
||||||
|
elseif tp == "number" then
|
||||||
|
-- Serialize integers with string.format to prevent
|
||||||
|
-- scientific notation, which doesn't preserve
|
||||||
|
-- precision and breaks things like node position
|
||||||
|
-- hashes. Serialize floats normally.
|
||||||
|
if math.floor(x) == x then
|
||||||
|
return string.format("%d", x)
|
||||||
|
else
|
||||||
|
return tostring(x)
|
||||||
|
end
|
||||||
|
elseif tp == "table" then
|
||||||
|
local vals = {}
|
||||||
|
local idx_dumped = {}
|
||||||
|
local np = nest_points[x]
|
||||||
|
for i, v in ipairs(x) do
|
||||||
|
if not np or not np[i] then
|
||||||
|
vals[#vals + 1] = dump_or_ref_val(v)
|
||||||
|
end
|
||||||
|
idx_dumped[i] = true
|
||||||
|
end
|
||||||
|
for k, v in pairs(x) do
|
||||||
|
if (not np or not np[k]) and
|
||||||
|
not idx_dumped[k] then
|
||||||
|
vals[#vals + 1] = "["..dump_or_ref_val(k).."] = "
|
||||||
|
..dump_or_ref_val(v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return "{"..table.concat(vals, ", ").."}"
|
||||||
|
else
|
||||||
|
error("Can't serialize data of type "..tp)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function dump_nest_points()
|
||||||
|
for parent, vals in pairs(nest_points) do
|
||||||
|
for k, v in pairs(vals) do
|
||||||
|
local_defs[#local_defs + 1] = dump_or_ref_val(parent)
|
||||||
|
.."["..dump_or_ref_val(k).."] = "
|
||||||
|
..dump_or_ref_val(v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
mark_multiple_occurences(x)
|
||||||
|
local top_level = dump_or_ref_val(x)
|
||||||
|
dump_nest_points()
|
||||||
|
|
||||||
|
if next(local_defs) then
|
||||||
|
return "local _ = {}\n"
|
||||||
|
..table.concat(local_defs, "\n")
|
||||||
|
.."\nreturn "..top_level
|
||||||
|
else
|
||||||
|
return "return "..top_level
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Deserialization
|
||||||
|
|
||||||
|
local env = {
|
||||||
|
loadstring = loadstring,
|
||||||
|
}
|
||||||
|
|
||||||
|
local safe_env = {
|
||||||
|
loadstring = function() end,
|
||||||
|
}
|
||||||
|
|
||||||
|
function core.deserialize(str, safe)
|
||||||
|
if type(str) ~= "string" then
|
||||||
|
return nil, "Cannot deserialize type '"..type(str)
|
||||||
|
.."'. Argument must be a string."
|
||||||
|
end
|
||||||
|
if str:byte(1) == 0x1B then
|
||||||
|
return nil, "Bytecode prohibited"
|
||||||
|
end
|
||||||
|
local f, err = loadstring(str)
|
||||||
|
if not f then return nil, err end
|
||||||
|
setfenv(f, safe and safe_env or env)
|
||||||
|
|
||||||
|
local good, data = pcall(f)
|
||||||
|
if good then
|
||||||
|
return data
|
||||||
|
else
|
||||||
|
return nil, data
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- Unit tests
|
||||||
|
local test_in = {cat={sound="nyan", speed=400}, dog={sound="woof"}}
|
||||||
|
local test_out = core.deserialize(core.serialize(test_in))
|
||||||
|
|
||||||
|
assert(test_in.cat.sound == test_out.cat.sound)
|
||||||
|
assert(test_in.cat.speed == test_out.cat.speed)
|
||||||
|
assert(test_in.dog.sound == test_out.dog.sound)
|
||||||
|
|
||||||
|
test_in = {escape_chars="\n\r\t\v\\\"\'", non_european="θשׁ٩∂"}
|
||||||
|
test_out = core.deserialize(core.serialize(test_in))
|
||||||
|
assert(test_in.escape_chars == test_out.escape_chars)
|
||||||
|
assert(test_in.non_european == test_out.non_european)
|
||||||
|
|
152
track_defs.lua
Normal file
152
track_defs.lua
Normal file
@ -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 ==--
|
265
tracks.lua
Normal file
265
tracks.lua
Normal file
@ -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
|
||||||
|
models_suffix (with dot)
|
||||||
|
[shared_model]
|
||||||
|
formats={
|
||||||
|
st,cr,swlst,swlcr,swrst,swrcr,vst1,vst2
|
||||||
|
(each a table with indices 0-3, for if to register a rail with this 'rotation' table entry. nil is assumed as 'all', set {} to not register at all)
|
||||||
|
}
|
||||||
|
common={} change something on common rail appearance
|
||||||
|
}
|
||||||
|
[18.12.17] Note on new connection system:
|
||||||
|
In order to support real rail crossing nodes and finally make the trackplacer respect switches, I changed the connection system.
|
||||||
|
There can be a variable number of connections available. These are specified as tuples {c=<connection>, y=<rely>}
|
||||||
|
The table "at_conns" consists of {<conn1>, <conn2>...}
|
||||||
|
the "at_rail_y" property holds the value that was previously called "railheight"
|
||||||
|
Depending on the number of connections:
|
||||||
|
2 conns: regular rail
|
||||||
|
3 conns: switch:
|
||||||
|
- when train passes in at conn1, will move out of conn2
|
||||||
|
- when train passes in at conn2 or conn3, will move out of conn1
|
||||||
|
4 conns: cross (or cross switch, depending on arrangement of conns):
|
||||||
|
- conn1 <> conn2
|
||||||
|
- conn3 <> conn4
|
||||||
|
]]
|
||||||
|
|
||||||
|
function advtrains.register_tracks(tracktype, def, preset)
|
||||||
|
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
|
||||||
|
|
||||||
|
--connections
|
||||||
|
local at_conns = advtrains.rotate_conn_by(var.conns, (rotid-1)*preset.regstep)
|
||||||
|
|
||||||
|
trackconns[def.nodename_prefix.."_"..suffix..rotation] = at_conns
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function advtrains.get_track_connections(name, param2)
|
||||||
|
|
||||||
|
if not trackconns[name] then return end
|
||||||
|
|
||||||
|
return advtrains.rotate_conn_by(trackconns[name], param2*AT_CMAX/4), nil, nil
|
||||||
|
end
|
145
vector.lua
Executable file
145
vector.lua
Executable file
@ -0,0 +1,145 @@
|
|||||||
|
|
||||||
|
vector = {}
|
||||||
|
|
||||||
|
function vector.new(a, b, c)
|
||||||
|
if type(a) == "table" then
|
||||||
|
assert(a.x and a.y and a.z, "Invalid vector passed to vector.new()")
|
||||||
|
return {x=a.x, y=a.y, z=a.z}
|
||||||
|
elseif a then
|
||||||
|
assert(b and c, "Invalid arguments for vector.new()")
|
||||||
|
return {x=a, y=b, z=c}
|
||||||
|
end
|
||||||
|
return {x=0, y=0, z=0}
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector.equals(a, b)
|
||||||
|
return a.x == b.x and
|
||||||
|
a.y == b.y and
|
||||||
|
a.z == b.z
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector.length(v)
|
||||||
|
return math.hypot(v.x, math.hypot(v.y, v.z))
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector.normalize(v)
|
||||||
|
local len = vector.length(v)
|
||||||
|
if len == 0 then
|
||||||
|
return {x=0, y=0, z=0}
|
||||||
|
else
|
||||||
|
return vector.divide(v, len)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector.floor(v)
|
||||||
|
return {
|
||||||
|
x = math.floor(v.x),
|
||||||
|
y = math.floor(v.y),
|
||||||
|
z = math.floor(v.z)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector.round(v)
|
||||||
|
return {
|
||||||
|
x = math.floor(v.x + 0.5),
|
||||||
|
y = math.floor(v.y + 0.5),
|
||||||
|
z = math.floor(v.z + 0.5)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector.apply(v, func)
|
||||||
|
return {
|
||||||
|
x = func(v.x),
|
||||||
|
y = func(v.y),
|
||||||
|
z = func(v.z)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector.distance(a, b)
|
||||||
|
local x = a.x - b.x
|
||||||
|
local y = a.y - b.y
|
||||||
|
local z = a.z - b.z
|
||||||
|
return math.hypot(x, math.hypot(y, z))
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector.direction(pos1, pos2)
|
||||||
|
local x_raw = pos2.x - pos1.x
|
||||||
|
local y_raw = pos2.y - pos1.y
|
||||||
|
local z_raw = pos2.z - pos1.z
|
||||||
|
local x_abs = math.abs(x_raw)
|
||||||
|
local y_abs = math.abs(y_raw)
|
||||||
|
local z_abs = math.abs(z_raw)
|
||||||
|
if x_abs >= y_abs and
|
||||||
|
x_abs >= z_abs then
|
||||||
|
y_raw = y_raw * (1 / x_abs)
|
||||||
|
z_raw = z_raw * (1 / x_abs)
|
||||||
|
x_raw = x_raw / x_abs
|
||||||
|
end
|
||||||
|
if y_abs >= x_abs and
|
||||||
|
y_abs >= z_abs then
|
||||||
|
x_raw = x_raw * (1 / y_abs)
|
||||||
|
z_raw = z_raw * (1 / y_abs)
|
||||||
|
y_raw = y_raw / y_abs
|
||||||
|
end
|
||||||
|
if z_abs >= y_abs and
|
||||||
|
z_abs >= x_abs then
|
||||||
|
x_raw = x_raw * (1 / z_abs)
|
||||||
|
y_raw = y_raw * (1 / z_abs)
|
||||||
|
z_raw = z_raw / z_abs
|
||||||
|
end
|
||||||
|
return {x=x_raw, y=y_raw, z=z_raw}
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function vector.add(a, b)
|
||||||
|
if type(b) == "table" then
|
||||||
|
return {x = a.x + b.x,
|
||||||
|
y = a.y + b.y,
|
||||||
|
z = a.z + b.z}
|
||||||
|
else
|
||||||
|
return {x = a.x + b,
|
||||||
|
y = a.y + b,
|
||||||
|
z = a.z + b}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector.subtract(a, b)
|
||||||
|
if type(b) == "table" then
|
||||||
|
return {x = a.x - b.x,
|
||||||
|
y = a.y - b.y,
|
||||||
|
z = a.z - b.z}
|
||||||
|
else
|
||||||
|
return {x = a.x - b,
|
||||||
|
y = a.y - b,
|
||||||
|
z = a.z - b}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector.multiply(a, b)
|
||||||
|
if type(b) == "table" then
|
||||||
|
return {x = a.x * b.x,
|
||||||
|
y = a.y * b.y,
|
||||||
|
z = a.z * b.z}
|
||||||
|
else
|
||||||
|
return {x = a.x * b,
|
||||||
|
y = a.y * b,
|
||||||
|
z = a.z * b}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector.divide(a, b)
|
||||||
|
if type(b) == "table" then
|
||||||
|
return {x = a.x / b.x,
|
||||||
|
y = a.y / b.y,
|
||||||
|
z = a.z / b.z}
|
||||||
|
else
|
||||||
|
return {x = a.x / b,
|
||||||
|
y = a.y / b,
|
||||||
|
z = a.z / b}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector.sort(a, b)
|
||||||
|
return {x = math.min(a.x, b.x), y = math.min(a.y, b.y), z = math.min(a.z, b.z)},
|
||||||
|
{x = math.max(a.x, b.x), y = math.max(a.y, b.y), z = math.max(a.z, b.z)}
|
||||||
|
end
|
Loading…
Reference in New Issue
Block a user