created git repository
|
@ -0,0 +1,138 @@
|
|||
|
||||
minetest.register_tool("advtrains:1",
|
||||
{
|
||||
description = "1",
|
||||
groups = {cracky=1}, -- key=name, value=rating; rating=1..3.
|
||||
inventory_image = "drwho_screwdriver.png",
|
||||
wield_image = "drwho_screwdriver.png",
|
||||
stack_max = 1,
|
||||
range = 7.0,
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
pos1=pointed_thing.under
|
||||
end,
|
||||
})
|
||||
minetest.register_tool("advtrains:2",
|
||||
{
|
||||
description = "2",
|
||||
groups = {cracky=1}, -- key=name, value=rating; rating=1..3.
|
||||
inventory_image = "drwho_screwdriver.png",
|
||||
wield_image = "drwho_screwdriver.png",
|
||||
stack_max = 1,
|
||||
range = 7.0,
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
pos2=pointed_thing.under
|
||||
end,
|
||||
})
|
||||
minetest.register_tool("advtrains:3",
|
||||
{
|
||||
description = "3",
|
||||
groups = {cracky=1}, -- key=name, value=rating; rating=1..3.
|
||||
inventory_image = "drwho_screwdriver.png",
|
||||
wield_image = "drwho_screwdriver.png",
|
||||
stack_max = 1,
|
||||
range = 7.0,
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
pos3=pointed_thing.under
|
||||
end,
|
||||
})
|
||||
minetest.register_tool("advtrains:4",
|
||||
{
|
||||
description = "4",
|
||||
groups = {cracky=1}, -- key=name, value=rating; rating=1..3.
|
||||
inventory_image = "drwho_screwdriver.png",
|
||||
wield_image = "drwho_screwdriver.png",
|
||||
stack_max = 1,
|
||||
range = 7.0,
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
pos4=pointed_thing.under
|
||||
end,
|
||||
})
|
||||
core.register_chatcommand("mad", {
|
||||
params="",
|
||||
description="",
|
||||
privs={},
|
||||
func = function(name, param)
|
||||
return true, advtrains.get_wagon_yaw(pos1, pos2, pos3, pos4, tonumber(param))*360/(2*math.pi)
|
||||
end,
|
||||
})
|
||||
|
||||
advtrains.firstobject=nil
|
||||
minetest.register_tool("advtrains:connect",
|
||||
{
|
||||
description = "connect wagons tool",
|
||||
groups = {cracky=1}, -- key=name, value=rating; rating=1..3.
|
||||
inventory_image = "drwho_screwdriver.png",
|
||||
wield_image = "drwho_screwdriver.png",
|
||||
stack_max = 1,
|
||||
range = 7.0,
|
||||
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
|
||||
end,
|
||||
--[[
|
||||
^ Shall place item and return the leftover itemstack
|
||||
^ default: minetest.item_place ]]
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
if pointed_thing.type=="object" then
|
||||
local luaent=pointed_thing.ref:get_luaentity()
|
||||
if luaent and luaent.is_wagon then
|
||||
if advtrains.firstobject then
|
||||
minetest.chat_send_all("connect second object "..luaent.unique_id)
|
||||
advtrains.connect_wagons(luaent, advtrains.firstobject)
|
||||
minetest.chat_send_all("done")
|
||||
advtrains.firstobject=nil
|
||||
else
|
||||
advtrains.firstobject=luaent
|
||||
minetest.chat_send_all("connect first object "..luaent.unique_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
--[[
|
||||
^ default: nil
|
||||
^ Function must return either nil if no item shall be removed from
|
||||
inventory, or an itemstack to replace the original itemstack.
|
||||
e.g. itemstack:take_item(); return itemstack
|
||||
^ Otherwise, the function is free to do what it wants.
|
||||
^ The default functions handle regular use cases.
|
||||
]]
|
||||
})
|
||||
minetest.register_tool("advtrains:tttool",
|
||||
{
|
||||
description = "traintester tool",
|
||||
groups = {cracky=1}, -- key=name, value=rating; rating=1..3.
|
||||
inventory_image = "drwho_screwdriver.png",
|
||||
wield_image = "drwho_screwdriver.png",
|
||||
stack_max = 1,
|
||||
range = 7.0,
|
||||
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
|
||||
end,
|
||||
--[[
|
||||
^ Shall place item and return the leftover itemstack
|
||||
^ default: minetest.item_place ]]
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
if pointed_thing.type=="object" then
|
||||
local luaent=pointed_thing.ref:get_luaentity()
|
||||
if luaent and luaent.is_wagon then
|
||||
minetest.chat_send_all("wagon yaw is "..pointed_thing.ref:getyaw())
|
||||
minetest.chat_send_all("trains last yaw is set to "..luaent:train().last_front_yaw)
|
||||
minetest.chat_send_all("end report")
|
||||
end
|
||||
else
|
||||
minetest.chat_send_all(dump(minetest.get_node(pointed_thing.under)))
|
||||
local c1, c2=advtrains.get_track_connections(minetest.get_node(pointed_thing.under).name, minetest.get_node(pointed_thing.under).param2)
|
||||
minetest.chat_send_all(c1.." <-> "..c2)
|
||||
end
|
||||
end,
|
||||
--[[
|
||||
^ default: nil
|
||||
^ Function must return either nil if no item shall be removed from
|
||||
inventory, or an itemstack to replace the original itemstack.
|
||||
e.g. itemstack:take_item(); return itemstack
|
||||
^ Otherwise, the function is free to do what it wants.
|
||||
^ The default functions handle regular use cases.
|
||||
]]
|
||||
}
|
||||
)
|
|
@ -0,0 +1,138 @@
|
|||
|
||||
minetest.register_tool("advtrains:1",
|
||||
{
|
||||
description = "1",
|
||||
groups = {cracky=1}, -- key=name, value=rating; rating=1..3.
|
||||
inventory_image = "drwho_screwdriver.png",
|
||||
wield_image = "drwho_screwdriver.png",
|
||||
stack_max = 1,
|
||||
range = 7.0,
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
pos1=pointed_thing.under
|
||||
end,
|
||||
})
|
||||
minetest.register_tool("advtrains:2",
|
||||
{
|
||||
description = "2",
|
||||
groups = {cracky=1}, -- key=name, value=rating; rating=1..3.
|
||||
inventory_image = "drwho_screwdriver.png",
|
||||
wield_image = "drwho_screwdriver.png",
|
||||
stack_max = 1,
|
||||
range = 7.0,
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
pos2=pointed_thing.under
|
||||
end,
|
||||
})
|
||||
minetest.register_tool("advtrains:3",
|
||||
{
|
||||
description = "3",
|
||||
groups = {cracky=1}, -- key=name, value=rating; rating=1..3.
|
||||
inventory_image = "drwho_screwdriver.png",
|
||||
wield_image = "drwho_screwdriver.png",
|
||||
stack_max = 1,
|
||||
range = 7.0,
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
pos3=pointed_thing.under
|
||||
end,
|
||||
})
|
||||
minetest.register_tool("advtrains:4",
|
||||
{
|
||||
description = "4",
|
||||
groups = {cracky=1}, -- key=name, value=rating; rating=1..3.
|
||||
inventory_image = "drwho_screwdriver.png",
|
||||
wield_image = "drwho_screwdriver.png",
|
||||
stack_max = 1,
|
||||
range = 7.0,
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
pos4=pointed_thing.under
|
||||
end,
|
||||
})
|
||||
core.register_chatcommand("mad", {
|
||||
params="",
|
||||
description="",
|
||||
privs={},
|
||||
func = function(name, param)
|
||||
return true, advtrains.get_wagon_yaw(pos1, pos2, pos3, pos4, tonumber(param))*2*math.pi/360
|
||||
end,
|
||||
})
|
||||
|
||||
advtrains.firstobject=nil
|
||||
minetest.register_tool("advtrains:connect",
|
||||
{
|
||||
description = "connect wagons tool",
|
||||
groups = {cracky=1}, -- key=name, value=rating; rating=1..3.
|
||||
inventory_image = "drwho_screwdriver.png",
|
||||
wield_image = "drwho_screwdriver.png",
|
||||
stack_max = 1,
|
||||
range = 7.0,
|
||||
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
|
||||
end,
|
||||
--[[
|
||||
^ Shall place item and return the leftover itemstack
|
||||
^ default: minetest.item_place ]]
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
if pointed_thing.type=="object" then
|
||||
local luaent=pointed_thing.ref:get_luaentity()
|
||||
if luaent and luaent.is_wagon then
|
||||
if advtrains.firstobject then
|
||||
minetest.chat_send_all("connect second object "..luaent.unique_id)
|
||||
advtrains.connect_wagons(luaent, advtrains.firstobject)
|
||||
minetest.chat_send_all("done")
|
||||
advtrains.firstobject=nil
|
||||
else
|
||||
advtrains.firstobject=luaent
|
||||
minetest.chat_send_all("connect first object "..luaent.unique_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
--[[
|
||||
^ default: nil
|
||||
^ Function must return either nil if no item shall be removed from
|
||||
inventory, or an itemstack to replace the original itemstack.
|
||||
e.g. itemstack:take_item(); return itemstack
|
||||
^ Otherwise, the function is free to do what it wants.
|
||||
^ The default functions handle regular use cases.
|
||||
]]
|
||||
})
|
||||
minetest.register_tool("advtrains:tttool",
|
||||
{
|
||||
description = "traintester tool",
|
||||
groups = {cracky=1}, -- key=name, value=rating; rating=1..3.
|
||||
inventory_image = "drwho_screwdriver.png",
|
||||
wield_image = "drwho_screwdriver.png",
|
||||
stack_max = 1,
|
||||
range = 7.0,
|
||||
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
|
||||
end,
|
||||
--[[
|
||||
^ Shall place item and return the leftover itemstack
|
||||
^ default: minetest.item_place ]]
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
if pointed_thing.type=="object" then
|
||||
local luaent=pointed_thing.ref:get_luaentity()
|
||||
if luaent and luaent.is_wagon then
|
||||
minetest.chat_send_all("wagon yaw is "..pointed_thing.ref:getyaw())
|
||||
minetest.chat_send_all("trains last yaw is set to "..luaent:train().last_front_yaw)
|
||||
minetest.chat_send_all("end report")
|
||||
end
|
||||
else
|
||||
minetest.chat_send_all(dump(minetest.get_node(pointed_thing.under)))
|
||||
local c1, c2=advtrains.get_track_connections(minetest.get_node(pointed_thing.under).name, minetest.get_node(pointed_thing.under).param2)
|
||||
minetest.chat_send_all(c1.." <-> "..c2)
|
||||
end
|
||||
end,
|
||||
--[[
|
||||
^ default: nil
|
||||
^ Function must return either nil if no item shall be removed from
|
||||
inventory, or an itemstack to replace the original itemstack.
|
||||
e.g. itemstack:take_item(); return itemstack
|
||||
^ Otherwise, the function is free to do what it wants.
|
||||
^ The default functions handle regular use cases.
|
||||
]]
|
||||
}
|
||||
)
|
|
@ -0,0 +1,215 @@
|
|||
--advtrains by orwell96, see readme.txt
|
||||
local print=function(t) minetest.log("action", t) minetest.chat_send_all(t) end
|
||||
|
||||
function advtrains.dirCoordSet(coord, dir)
|
||||
local x=0
|
||||
local z=0
|
||||
--local dir=(dirx+2)%8
|
||||
if(dir==6) then
|
||||
x=-1
|
||||
elseif (dir==7) then
|
||||
x=-1
|
||||
z=1
|
||||
elseif (dir==0) then
|
||||
z=1
|
||||
elseif (dir==1) then
|
||||
z=1
|
||||
x=1
|
||||
elseif (dir==2) then
|
||||
x=1
|
||||
elseif (dir==3) then
|
||||
x=1
|
||||
z=-1
|
||||
elseif (dir==4) then
|
||||
z=-1
|
||||
elseif (dir==5) then
|
||||
z=-1
|
||||
x=-1
|
||||
else
|
||||
error("advtrains: in helpers.lua/dirCoordSet() given dir="..(dir or "nil"))
|
||||
end
|
||||
|
||||
|
||||
return {x=coord.x+x, y=coord.y, z=coord.z+z}
|
||||
end
|
||||
function advtrains.dirToCoord(dir)
|
||||
return advtrains.dirCoordSet({x=0, y=0, z=0}, dir)
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
--vertical_transmit:
|
||||
--[[
|
||||
rely1, rely2 tell to which height the connections are pointed to. 1 means it will go up the next node
|
||||
|
||||
]]
|
||||
|
||||
function advtrains.conway(midreal, prev, drives_on)--in order prev,mid,return
|
||||
local mid=advtrains.round_vector_floor_y(midreal)
|
||||
if(not advtrains.is_track_and_drives_on(minetest.get_node(mid).name, drives_on)) then
|
||||
--print("[advtrains]in conway: no rail, returning!")
|
||||
return nil
|
||||
end
|
||||
if(not prev or not advtrains.is_track_and_drives_on(minetest.get_node(advtrains.round_vector_floor_y(prev)).name, drives_on)) then
|
||||
--print("[advtrains]in conway: no prev rail, there should be an initial path!, returning!")
|
||||
return nil
|
||||
end
|
||||
local midnode=minetest.get_node_or_nil(mid)
|
||||
if not midnode then --print("[advtrains][conway] midnode is ignore")
|
||||
return nil
|
||||
end
|
||||
|
||||
local middir1, middir2, midrely1, midrely2=advtrains.get_track_connections(midnode.name, midnode.param2)
|
||||
|
||||
local next, chkdir, chkrely, y_offset
|
||||
y_offset=0
|
||||
--print("[advtrains] in order mid1,mid2",middir1,middir2)
|
||||
--try if it is dir1
|
||||
local cor1=advtrains.dirCoordSet(mid, middir2)--<<<<
|
||||
if math.floor(cor1.x+0.5)==math.floor(prev.x+0.5) and math.floor(cor1.z+0.5)==math.floor(prev.z+0.5) then--this was previous
|
||||
next=advtrains.dirCoordSet(mid, middir1)
|
||||
if midrely1>=1 then
|
||||
next.y=next.y+1
|
||||
--print("[advtrains]found midrely1 to be >=1: next is now "..(next and minetest.pos_to_string(next) or "nil"))
|
||||
y_offset=1
|
||||
end
|
||||
chkdir=middir1
|
||||
chkrely=midrely1
|
||||
--print("[advtrains]dir2 applied next pos:",minetest.pos_to_string(next),"(chkdir is ",chkdir,")")
|
||||
end
|
||||
--dir2???
|
||||
local cor2=advtrains.dirCoordSet(mid, middir1)--<<<<
|
||||
if math.floor(cor2.x+0.5)==math.floor(prev.x+0.5) and math.floor(cor2.z+0.5)==math.floor(prev.z+0.5) then
|
||||
next=advtrains.dirCoordSet(mid, middir2)--dir2 wird überprüft, alles gut.
|
||||
if midrely2>=1 then
|
||||
next.y=next.y+1
|
||||
--print("[advtrains]found midrely2 to be >=1: next is now "..(next and minetest.pos_to_string(next) or "nil"))
|
||||
y_offset=1
|
||||
end
|
||||
chkdir=middir2
|
||||
chkrely=midrely2
|
||||
--print("[advtrains] dir2 applied next pos:",minetest.pos_to_string(next),"(chkdir is ",chkdir,")")
|
||||
end
|
||||
--print("[advtrains]dir applied next pos: "..(next and minetest.pos_to_string(next) or "nil").."(chkdir is "..(chkdir or "nil")..", y-offset "..y_offset..")")
|
||||
--is there a next
|
||||
if not next then
|
||||
print("[advtrains]in conway: no next rail(nil), returning!")
|
||||
return nil
|
||||
end
|
||||
local nextnode=minetest.get_node_or_nil(advtrains.round_vector_floor_y(next))
|
||||
if not nextnode then print("[advtrains][conway] nextnode is ignore")
|
||||
return nil
|
||||
end
|
||||
|
||||
--is it a rail?
|
||||
if(not advtrains.is_track_and_drives_on(nextnode.name, drives_on)) then
|
||||
print("[advtrains]in conway: next "..minetest.pos_to_string(next).." not a rail, trying one node below!")
|
||||
next.y=next.y-1
|
||||
y_offset=y_offset-1
|
||||
|
||||
nextnode=minetest.get_node_or_nil(advtrains.round_vector_floor_y(next))
|
||||
if not nextnode then --print("[advtrains][conway] nextnode is ignore")
|
||||
return nil
|
||||
end
|
||||
if(not advtrains.is_track_and_drives_on(nextnode.name, drives_on)) then
|
||||
print("[advtrains]in conway: one below "..minetest.pos_to_string(next).." is not a rail either, returning!")
|
||||
return nil
|
||||
end
|
||||
end
|
||||
--print("[advtrains]trying to find if rail connects: "..(next and minetest.pos_to_string(next) or "nil").."(chkdir is "..(chkdir or "nil")..", y-offset "..y_offset..")")
|
||||
|
||||
local nextdir1, nextdir2, nextrely1, nextrely2, nextrailheight=advtrains.get_track_connections(nextnode.name, nextnode.param2)
|
||||
|
||||
--is this next rail connecting to the mid?
|
||||
if not ( (((nextdir1+4)%8)==chkdir and nextrely1==chkrely-y_offset) or (((nextdir2+4)%8)==chkdir and nextrely2==chkrely-y_offset) ) then
|
||||
print("[advtrains]in conway: next "..minetest.pos_to_string(next).." not connecting, trying one node below!")
|
||||
next.y=next.y-1
|
||||
y_offset=y_offset-1
|
||||
|
||||
nextdir1, nextdir2, nextrely1, nextrely2, nextrailheight=advtrains.get_track_connections(nextnode.name, nextnode.param2)
|
||||
if not ( (((nextdir1+4)%8)==chkdir and nextrely1==chkrely) or (((nextdir2+4)%8)==chkdir and nextrely2==chkrely) ) then
|
||||
print("[advtrains]in conway: next "..minetest.pos_to_string(next).." rail not connecting, returning!")
|
||||
--print("[advtrains] in order mid1,2,next1,2,chkdir "..middir1.." "..middir2.." "..nextdir1.." "..nextdir2.." "..chkdir)
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
--print("[advtrains]conway found rail.")
|
||||
return vector.add(advtrains.round_vector_floor_y(next), {x=0, y=nextrailheight, z=0}), chkdir
|
||||
end
|
||||
|
||||
function advtrains.round_vector_floor_y(vec)
|
||||
return {x=math.floor(vec.x+0.5), y=math.floor(vec.y), z=math.floor(vec.z+0.5)}
|
||||
end
|
||||
|
||||
function advtrains.yawToDirection(yaw, conn1, conn2)
|
||||
if not conn1 or not conn2 then
|
||||
error("given nil to yawToDirection: conn1="..(conn1 or "nil").." conn2="..(conn1 or "nil"))
|
||||
end
|
||||
local yaw1=math.pi*(conn1/4)
|
||||
local yaw2=math.pi*(conn2/4)
|
||||
if advtrains.minAngleDiffRad(yaw, yaw1)<advtrains.minAngleDiffRad(yaw, yaw2) then--change to > if weird behavior
|
||||
return conn2
|
||||
else
|
||||
return conn1
|
||||
end
|
||||
end
|
||||
|
||||
function advtrains.minAngleDiffRad(r1, r2)
|
||||
local try1=r2-r1
|
||||
local try2=(r2+2*math.pi)-r1
|
||||
local try3=r2-(r1+2*math.pi)
|
||||
if math.min(math.abs(try1), math.abs(try2), math.abs(try3))==math.abs(try1) then
|
||||
return try1
|
||||
end
|
||||
if math.min(math.abs(try1), math.abs(try2), math.abs(try3))==math.abs(try2) then
|
||||
return try2
|
||||
end
|
||||
if math.min(math.abs(try1), math.abs(try2), math.abs(try3))==math.abs(try3) then
|
||||
return try3
|
||||
end
|
||||
end
|
||||
function advtrains.dumppath(path)
|
||||
if not path then print("dumppath: no path(nil)") return end
|
||||
local min=advtrains.minN(path)
|
||||
local max=advtrains.maxN(path)
|
||||
for i=min, max do print("["..i.."] "..(path[i] and minetest.pos_to_string(path[i]) or "nil")) end
|
||||
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.yaw_from_3_positions(prev, curr, next)
|
||||
local pts=minetest.pos_to_string
|
||||
--print("p3 "..pts(prev)..pts(curr)..pts(next))
|
||||
local prev2curr=math.atan2((curr.x-prev.x), (prev.z-curr.z))
|
||||
local curr2next=math.atan2((next.x-curr.x), (curr.z-next.z))
|
||||
--print("y3 "..(prev2curr*360/(2*math.pi)).." "..(curr2next*360/(2*math.pi)))
|
||||
return prev2curr+(advtrains.minAngleDiffRad(prev2curr, curr2next)/2)
|
||||
end
|
||||
function advtrains.get_wagon_yaw(front, first, second, back, pct)
|
||||
local pts=minetest.pos_to_string
|
||||
--print("p "..pts(front)..pts(first)..pts(second)..pts(back))
|
||||
local y2=advtrains.yaw_from_3_positions(second, first, front)
|
||||
local y1=advtrains.yaw_from_3_positions(back, second, first)
|
||||
--print("y "..(y1*360/(2*math.pi)).." "..(y2*360/(2*math.pi)))
|
||||
return y1+advtrains.minAngleDiffRad(y1, y2)*pct
|
||||
end
|
|
@ -0,0 +1,215 @@
|
|||
--advtrains by orwell96, see readme.txt
|
||||
local print=function(t) minetest.log("action", t) minetest.chat_send_all(t) end
|
||||
|
||||
function advtrains.dirCoordSet(coord, dir)
|
||||
local x=0
|
||||
local z=0
|
||||
--local dir=(dirx+2)%8
|
||||
if(dir==6) then
|
||||
x=-1
|
||||
elseif (dir==7) then
|
||||
x=-1
|
||||
z=1
|
||||
elseif (dir==0) then
|
||||
z=1
|
||||
elseif (dir==1) then
|
||||
z=1
|
||||
x=1
|
||||
elseif (dir==2) then
|
||||
x=1
|
||||
elseif (dir==3) then
|
||||
x=1
|
||||
z=-1
|
||||
elseif (dir==4) then
|
||||
z=-1
|
||||
elseif (dir==5) then
|
||||
z=-1
|
||||
x=-1
|
||||
else
|
||||
error("advtrains: in helpers.lua/dirCoordSet() given dir="..(dir or "nil"))
|
||||
end
|
||||
|
||||
|
||||
return {x=coord.x+x, y=coord.y, z=coord.z+z}
|
||||
end
|
||||
function advtrains.dirToCoord(dir)
|
||||
return advtrains.dirCoordSet({x=0, y=0, z=0}, dir)
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
--vertical_transmit:
|
||||
--[[
|
||||
rely1, rely2 tell to which height the connections are pointed to. 1 means it will go up the next node
|
||||
|
||||
]]
|
||||
|
||||
function advtrains.conway(midreal, prev, drives_on)--in order prev,mid,return
|
||||
local mid=advtrains.round_vector_floor_y(midreal)
|
||||
if(not advtrains.is_track_and_drives_on(minetest.get_node(mid).name, drives_on)) then
|
||||
--print("[advtrains]in conway: no rail, returning!")
|
||||
return nil
|
||||
end
|
||||
if(not prev or not advtrains.is_track_and_drives_on(minetest.get_node(advtrains.round_vector_floor_y(prev)).name, drives_on)) then
|
||||
--print("[advtrains]in conway: no prev rail, there should be an initial path!, returning!")
|
||||
return nil
|
||||
end
|
||||
local midnode=minetest.get_node_or_nil(mid)
|
||||
if not midnode then --print("[advtrains][conway] midnode is ignore")
|
||||
return nil
|
||||
end
|
||||
|
||||
local middir1, middir2, midrely1, midrely2=advtrains.get_track_connections(midnode.name, midnode.param2)
|
||||
|
||||
local next, chkdir, chkrely, y_offset
|
||||
y_offset=0
|
||||
--print("[advtrains] in order mid1,mid2",middir1,middir2)
|
||||
--try if it is dir1
|
||||
local cor1=advtrains.dirCoordSet(mid, middir2)--<<<<
|
||||
if math.floor(cor1.x+0.5)==math.floor(prev.x+0.5) and math.floor(cor1.z+0.5)==math.floor(prev.z+0.5) then--this was previous
|
||||
next=advtrains.dirCoordSet(mid, middir1)
|
||||
if midrely1>=1 then
|
||||
next.y=next.y+1
|
||||
--print("[advtrains]found midrely1 to be >=1: next is now "..(next and minetest.pos_to_string(next) or "nil"))
|
||||
y_offset=1
|
||||
end
|
||||
chkdir=middir1
|
||||
chkrely=midrely1
|
||||
--print("[advtrains]dir2 applied next pos:",minetest.pos_to_string(next),"(chkdir is ",chkdir,")")
|
||||
end
|
||||
--dir2???
|
||||
local cor2=advtrains.dirCoordSet(mid, middir1)--<<<<
|
||||
if math.floor(cor2.x+0.5)==math.floor(prev.x+0.5) and math.floor(cor2.z+0.5)==math.floor(prev.z+0.5) then
|
||||
next=advtrains.dirCoordSet(mid, middir2)--dir2 wird überprüft, alles gut.
|
||||
if midrely2>=1 then
|
||||
next.y=next.y+1
|
||||
--print("[advtrains]found midrely2 to be >=1: next is now "..(next and minetest.pos_to_string(next) or "nil"))
|
||||
y_offset=1
|
||||
end
|
||||
chkdir=middir2
|
||||
chkrely=midrely2
|
||||
--print("[advtrains] dir2 applied next pos:",minetest.pos_to_string(next),"(chkdir is ",chkdir,")")
|
||||
end
|
||||
--print("[advtrains]dir applied next pos: "..(next and minetest.pos_to_string(next) or "nil").."(chkdir is "..(chkdir or "nil")..", y-offset "..y_offset..")")
|
||||
--is there a next
|
||||
if not next then
|
||||
print("[advtrains]in conway: no next rail(nil), returning!")
|
||||
return nil
|
||||
end
|
||||
local nextnode=minetest.get_node_or_nil(advtrains.round_vector_floor_y(next))
|
||||
if not nextnode then print("[advtrains][conway] nextnode is ignore")
|
||||
return nil
|
||||
end
|
||||
|
||||
--is it a rail?
|
||||
if(not advtrains.is_track_and_drives_on(nextnode.name, drives_on)) then
|
||||
print("[advtrains]in conway: next "..minetest.pos_to_string(next).." not a rail, trying one node below!")
|
||||
next.y=next.y-1
|
||||
y_offset=y_offset-1
|
||||
|
||||
nextnode=minetest.get_node_or_nil(advtrains.round_vector_floor_y(next))
|
||||
if not nextnode then --print("[advtrains][conway] nextnode is ignore")
|
||||
return nil
|
||||
end
|
||||
if(not advtrains.is_track_and_drives_on(nextnode.name, drives_on)) then
|
||||
print("[advtrains]in conway: one below "..minetest.pos_to_string(next).." is not a rail either, returning!")
|
||||
return nil
|
||||
end
|
||||
end
|
||||
--print("[advtrains]trying to find if rail connects: "..(next and minetest.pos_to_string(next) or "nil").."(chkdir is "..(chkdir or "nil")..", y-offset "..y_offset..")")
|
||||
|
||||
local nextdir1, nextdir2, nextrely1, nextrely2, nextrailheight=advtrains.get_track_connections(nextnode.name, nextnode.param2)
|
||||
|
||||
--is this next rail connecting to the mid?
|
||||
if not ( (((nextdir1+4)%8)==chkdir and nextrely1==chkrely-y_offset) or (((nextdir2+4)%8)==chkdir and nextrely2==chkrely-y_offset) ) then
|
||||
print("[advtrains]in conway: next "..minetest.pos_to_string(next).." not connecting, trying one node below!")
|
||||
next.y=next.y-1
|
||||
y_offset=y_offset-1
|
||||
|
||||
nextdir1, nextdir2, nextrely1, nextrely2, nextrailheight=advtrains.get_track_connections(nextnode.name, nextnode.param2)
|
||||
if not ( (((nextdir1+4)%8)==chkdir and nextrely1==chkrely) or (((nextdir2+4)%8)==chkdir and nextrely2==chkrely) ) then
|
||||
print("[advtrains]in conway: next "..minetest.pos_to_string(next).." rail not connecting, returning!")
|
||||
--print("[advtrains] in order mid1,2,next1,2,chkdir "..middir1.." "..middir2.." "..nextdir1.." "..nextdir2.." "..chkdir)
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
--print("[advtrains]conway found rail.")
|
||||
return vector.add(advtrains.round_vector_floor_y(next), {x=0, y=nextrailheight, z=0}), chkdir
|
||||
end
|
||||
|
||||
function advtrains.round_vector_floor_y(vec)
|
||||
return {x=math.floor(vec.x+0.5), y=math.floor(vec.y), z=math.floor(vec.z+0.5)}
|
||||
end
|
||||
|
||||
function advtrains.yawToDirection(yaw, conn1, conn2)
|
||||
if not conn1 or not conn2 then
|
||||
error("given nil to yawToDirection: conn1="..(conn1 or "nil").." conn2="..(conn1 or "nil"))
|
||||
end
|
||||
local yaw1=math.pi*(conn1/4)
|
||||
local yaw2=math.pi*(conn2/4)
|
||||
if advtrains.minAngleDiffRad(yaw, yaw1)<advtrains.minAngleDiffRad(yaw, yaw2) then--change to > if weird behavior
|
||||
return conn2
|
||||
else
|
||||
return conn1
|
||||
end
|
||||
end
|
||||
|
||||
function advtrains.minAngleDiffRad(r1, r2)
|
||||
local try1=r2-r1
|
||||
local try2=(r2+2*math.pi)-r1
|
||||
local try3=r2-(r1+2*math.pi)
|
||||
if math.min(math.abs(try1), math.abs(try2), math.abs(try3))==math.abs(try1) then
|
||||
return try1
|
||||
end
|
||||
if math.min(math.abs(try1), math.abs(try2), math.abs(try3))==math.abs(try2) then
|
||||
return try2
|
||||
end
|
||||
if math.min(math.abs(try1), math.abs(try2), math.abs(try3))==math.abs(try3) then
|
||||
return try3
|
||||
end
|
||||
end
|
||||
function advtrains.dumppath(path)
|
||||
if not path then print("dumppath: no path(nil)") return end
|
||||
local min=advtrains.minN(path)
|
||||
local max=advtrains.maxN(path)
|
||||
for i=min, max do print("["..i.."] "..(path[i] and minetest.pos_to_string(path[i]) or "nil")) end
|
||||
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.yaw_from_3_positions(prev, curr, next)
|
||||
local pts=minetest.pos_to_string
|
||||
print("p3 "..pts(prev)..pts(curr)..pts(next))
|
||||
local prev2curr=math.atan2((curr.x-prev.x), (prev.z-curr.z))
|
||||
local curr2next=math.atan2((next.x-curr.x), (curr.z-next.z))
|
||||
print("y3 "..(prev2curr*360/(2*math.pi)).." "..(curr2next*360/(2*math.pi)))
|
||||
return prev2curr+(advtrains.minAngleDiffRad(prev2curr, curr2next)/2)
|
||||
end
|
||||
function advtrains.get_wagon_yaw(front, first, second, back, pct)
|
||||
local pts=minetest.pos_to_string
|
||||
print("p "..pts(front)..pts(first)..pts(second)..pts(back))
|
||||
local y2=advtrains.yaw_from_3_positions(second, first, front)
|
||||
local y1=advtrains.yaw_from_3_positions(back, second, first)
|
||||
print("y "..(y1*360/(2*math.pi)).." "..(y2*360/(2*math.pi)))
|
||||
return y1+advtrains.minAngleDiffRad(y1, y2)*pct
|
||||
end
|
|
@ -0,0 +1,19 @@
|
|||
--advtrains
|
||||
|
||||
advtrains={}
|
||||
|
||||
advtrains.modpath = minetest.get_modpath("advtrains")
|
||||
|
||||
print=function(text)
|
||||
minetest.log("action", tostring(text) or "<non-string>")
|
||||
end
|
||||
|
||||
|
||||
dofile(advtrains.modpath.."/helpers.lua");
|
||||
dofile(advtrains.modpath.."/debugitems.lua");
|
||||
|
||||
dofile(advtrains.modpath.."/trainlogic.lua");
|
||||
dofile(advtrains.modpath.."/trainhud.lua")
|
||||
dofile(advtrains.modpath.."/trackplacer.lua")
|
||||
dofile(advtrains.modpath.."/tracks.lua")
|
||||
dofile(advtrains.modpath.."/wagons.lua")
|
|
@ -0,0 +1,19 @@
|
|||
--advtrains
|
||||
|
||||
advtrains={}
|
||||
|
||||
advtrains.modpath = minetest.get_modpath("advtrains")
|
||||
|
||||
print=function(text)
|
||||
minetest.log("action", tostring(text) or "<non-string>")
|
||||
end
|
||||
|
||||
|
||||
dofile(advtrains.modpath.."/helpers.lua");
|
||||
dofile(advtrains.modpath.."/debugitems.lua");
|
||||
|
||||
dofile(advtrains.modpath.."/trainlogic.lua");
|
||||
dofile(advtrains.modpath.."/trainhud.lua")
|
||||
dofile(advtrains.modpath.."/trackplacer.lua")
|
||||
dofile(advtrains.modpath.."/tracks.lua")
|
||||
dofile(advtrains.modpath.."/vehicles.lua")
|
After Width: | Height: | Size: 171 KiB |
After Width: | Height: | Size: 106 KiB |
After Width: | Height: | Size: 58 KiB |
After Width: | Height: | Size: 65 KiB |
|
@ -0,0 +1,121 @@
|
|||
|
||||
--pseudoload.lua
|
||||
--responsible for keeping up a database of all rail nodes existant in the world, regardless of whether the mapchunk is loaded.
|
||||
|
||||
advtrains.trackdb={}
|
||||
--trackdb[y][x][z]={conn1, conn2, rely1, rely2, railheight}
|
||||
--serialization format:
|
||||
--(2byte x)(2byte y)(2byte z)(4bits conn1, 4bits conn2)[(plain rely1)|(plain rely2)|(plain railheight)]\n
|
||||
--[] may be missing if 0,0,0
|
||||
|
||||
--load initially
|
||||
advtrains.pl_fpath=minetest.get_worldpath().."/advtrains_trackdb"
|
||||
local file, err = io.open(advtrains.pl_fpath, "r")
|
||||
if not file then
|
||||
local er=err or "Unknown Error"
|
||||
print("[advtrains]Failed loading advtrains trackdb save file "..er)
|
||||
else
|
||||
--custom format to save memory
|
||||
while true do
|
||||
local xbytes=file:read(2)
|
||||
if not xbytes then
|
||||
break --eof reached
|
||||
end
|
||||
local ybytes=file:read(2)
|
||||
local zbytes=file:read(2)
|
||||
local x=(string.byte(xbytes[1])-128)*256+(string.byte(xbytes[2]))
|
||||
local y=(string.byte(ybytes[1])-128)*256+(string.byte(ybytes[2]))
|
||||
local z=(string.byte(zbytes[1])-128)*256+(string.byte(zbytes[2]))
|
||||
|
||||
local conn1=string.byte(file:read(1))
|
||||
local conn1=string.byte(file:read(1))
|
||||
|
||||
if not advtrains.trackdb[y] then advtrains.trackdb[y]={} end
|
||||
if not advtrains.trackdb[y][x] then advtrains.trackdb[y][x]={} end
|
||||
|
||||
local rest=file.read("*l")
|
||||
if rest~="" then
|
||||
local rely1, rely2, railheight=string.match(rest, "([^|]+)|([^|]+)|([^|]+)")
|
||||
if rely1 and rely2 and railheight then
|
||||
advtrains.trackdb[y][x][z]={
|
||||
conn1=conn1, conn2=conn2,
|
||||
rely1=rely1, rely2=rely2,
|
||||
railheight=railheight
|
||||
}
|
||||
else
|
||||
advtrains.trackdb[y][x][z]={
|
||||
conn1=conn1, conn2=conn2
|
||||
}
|
||||
end
|
||||
else
|
||||
advtrains.trackdb[y][x][z]={
|
||||
conn1=conn1, conn2=conn2
|
||||
}
|
||||
end
|
||||
end
|
||||
file:close()
|
||||
end
|
||||
function advtrains.save_trackdb()
|
||||
local file, err = io.open(advtrains.pl_fpath, "w")
|
||||
if not file then
|
||||
local er=err or "Unknown Error"
|
||||
print("[advtrains]Failed saving advtrains trackdb save file "..er)
|
||||
else
|
||||
--custom format to save memory
|
||||
for x,txl in pairs(advtrains.trackdb) do
|
||||
for y,tyl in pairs(txl) do
|
||||
for z,rail in pairs(tyl) do
|
||||
file:write(string.char(math.floor(x/256)+128)..string.char((x%256)))
|
||||
file:write(string.char(math.floor(y/256)+128)..string.char((y%256)))
|
||||
file:write(string.char(math.floor(z/256)+128)..string.char((z%256)))
|
||||
file:write(string.char(rail.conn1))
|
||||
file:write(string.char(rail.conn2))
|
||||
if (rail.rely1 and rail.rely1~=0) or (rail.rely2 and rail.rely2~=0) or (rail.railheight and rail.railheight~=0) then
|
||||
file:write(rail.rely1.."|"..rail.rely2.."|"..rail.railheight)
|
||||
end
|
||||
file:write("\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
file:close()
|
||||
end
|
||||
end
|
||||
|
||||
--get_node with pseudoload.
|
||||
--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, traintype)
|
||||
local node=minetest.get_node_or_nil(pos)
|
||||
if not node then
|
||||
--try raildb
|
||||
local rdp=vector.round(rdp)
|
||||
local dbe=advtrains.trackdb[rdp.y][rdp.x][rdp.z]
|
||||
if dbe then
|
||||
return true, dbe.conn1, dbe.conn2, dbe.rely1 or 0, dbe.rely2 or 0, dbe.railheight or 0
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
local nodename=node.name
|
||||
if(not advtrains.is_track_and_drives_on(nodename, advtrains.all_traintypes[traintype].drives_on)) then
|
||||
return false
|
||||
end
|
||||
local conn1, conn2, rely1, rely2, railheight=advtrains.get_track_connections(node.name, node.param2)
|
||||
|
||||
--already in trackdb?
|
||||
local rdp=vector.round(rdp)
|
||||
if not advtrains.trackdb[rdp.y][rdp.x][rdp.z] then--TODO is this write prevention here really needed?
|
||||
advtrains.trackdb[rdp.y][rdp.x][rdp.z]={
|
||||
conn1=conn1, conn2=conn2,
|
||||
rely1=rely1, rely2=rely2,
|
||||
railheight=railheight
|
||||
}
|
||||
end
|
||||
|
||||
return true, conn1, conn2, rely1, rely2, railheight
|
||||
end
|
||||
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
|
||||
--pseudoload.lua
|
||||
--responsible for keeping up a database of all rail nodes existant in the world, regardless of whether the mapchunk is loaded.
|
||||
|
||||
advtrains.trackdb={}
|
||||
--trackdb[y][x][z]={conn1, conn2, rely1, rely2, railheight}
|
||||
--serialization format:
|
||||
--(2byte x)(2byte y)(2byte z)(4bits conn1, 4bits conn2)[(plain rely1)|(plain rely2)|(plain railheight)]\n
|
||||
--[] may be missing if 0,0,0
|
||||
|
||||
--load initially
|
||||
advtrains.pl_fpath=minetest.get_worldpath().."/advtrains_trackdb"
|
||||
local file, err = io.open(advtrains.pl_fpath, "r")
|
||||
if not file then
|
||||
local er=err or "Unknown Error"
|
||||
print("[advtrains]Failed loading advtrains trackdb save file "..er)
|
||||
else
|
||||
--custom format to save memory
|
||||
while true do
|
||||
local xbytes=file:read(2)
|
||||
if not xbytes then
|
||||
break --eof reached
|
||||
end
|
||||
local ybytes=file:read(2)
|
||||
local zbytes=file:read(2)
|
||||
local x=(string.byte(xbytes[1])-128)*256+(string.byte(xbytes[2]))
|
||||
local y=(string.byte(ybytes[1])-128)*256+(string.byte(ybytes[2]))
|
||||
local z=(string.byte(zbytes[1])-128)*256+(string.byte(zbytes[2]))
|
||||
|
||||
local conn1=string.byte(file:read(1))
|
||||
local conn1=string.byte(file:read(1))
|
||||
|
||||
if not advtrains.trackdb[y] then advtrains.trackdb[y]={} end
|
||||
if not advtrains.trackdb[y][x] then advtrains.trackdb[y][x]={} end
|
||||
|
||||
local rest=file.read("*l")
|
||||
if rest~="" then
|
||||
local rely1, rely2, railheight=string.match(rest, "([^|]+)|([^|]+)|([^|]+)")
|
||||
if rely1 and rely2 and railheight then
|
||||
advtrains.trackdb[y][x][z]={
|
||||
conn1=conn1, conn2=conn2,
|
||||
rely1=rely1, rely2=rely2,
|
||||
railheight=railheight
|
||||
}
|
||||
else
|
||||
advtrains.trackdb[y][x][z]={
|
||||
conn1=conn1, conn2=conn2
|
||||
}
|
||||
end
|
||||
else
|
||||
advtrains.trackdb[y][x][z]={
|
||||
conn1=conn1, conn2=conn2
|
||||
}
|
||||
end
|
||||
end
|
||||
file:close()
|
||||
end
|
||||
function advtrains.save_trackdb()
|
||||
local file, err = io.open(advtrains.pl_fpath, "w")
|
||||
if not file then
|
||||
local er=err or "Unknown Error"
|
||||
print("[advtrains]Failed saving advtrains trackdb save file "..er)
|
||||
else
|
||||
--custom format to save memory
|
||||
for x,txl in pairs(advtrains.trackdb) do
|
||||
for y,tyl in pairs(txl) do
|
||||
for z,rail in pairs(tyl) do
|
||||
file:write(string.char(math.floor(x/256)+128)..string.char((x%256)))
|
||||
file:write(string.char(math.floor(y/256)+128)..string.char((y%256)))
|
||||
file:write(string.char(math.floor(z/256)+128)..string.char((z%256)))
|
||||
file:write(string.char(rail.conn1))
|
||||
file:write(string.char(rail.conn2))
|
||||
if (rail.rely1 and rail.rely1~=0) or (rail.rely2 and rail.rely2~=0) or (rail.railheight and rail.railheight~=0) then
|
||||
file:write(rail.rely1.."|"..rail.rely2.."|"..rail.railheight)
|
||||
end
|
||||
file:write("\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
file:close()
|
||||
end
|
||||
end
|
||||
|
||||
--get_node with pseudoload.
|
||||
--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_at(pos, traintype)
|
||||
local node=minetest.get_node_or_nil(pos)
|
||||
if not node then
|
||||
--try raildb
|
||||
local rdp=vector.round(rdp)
|
||||
local dbe=advtrains.trackdb[rdp.y][rdp.x][rdp.z]
|
||||
if dbe then
|
||||
return true, dbe.conn1, dbe.conn2, dbe.rely1 or 0, dbe.rely2 or 0, dbe.railheight or 0
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
local nodename=node.name
|
||||
if(not advtrains.is_track_and_drives_on(nodename, advtrains.all_traintypes[traintype].drives_on)) then
|
||||
return false
|
||||
end
|
||||
local conn1, conn2, rely1, rely2, railheight=advtrains.get_track_connections(node.name, node.param2)
|
||||
|
||||
--already in trackdb?
|
||||
local rdp=vector.round(rdp)
|
||||
if not advtrains.trackdb[rdp.y][rdp.x][rdp.z] then--TODO is this write prevention here really needed?
|
||||
advtrains.trackdb[rdp.y][rdp.x][rdp.z]={
|
||||
conn1=conn1, conn2=conn2,
|
||||
rely1=rely1, rely2=rely2,
|
||||
railheight=railheight
|
||||
}
|
||||
end
|
||||
|
||||
return true, conn1, conn2, rely1, rely2, railheight
|
||||
end
|
||||
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
Hi
|
||||
Since there are no trains mods out there that satisfied my needs, I decided to write my own.
|
||||
Until now are supported:
|
||||
- tracks and switches, based on angles of 45 degrees
|
||||
- wagons (atm they all look like very simple locomotives in different colors) that drive on these rails and collide with nodes in the environment(they need 3x3x3 space)
|
||||
- conecting/disconnecting of wagons/trains
|
||||
Planned features:
|
||||
- trains will only move if a locomotive is in train
|
||||
- locomotives need coal to drive (and water...)
|
||||
- more types of trains and rails(electric, diesel, maglevs...)
|
||||
- better controls
|
||||
- cool models for trains and rails
|
||||
- an API, because API's are cool.
|
||||
(I will probably split trains api and actual trains into two mods, to allow for extensions to be enabled individually)
|
||||
|
||||
At the moment, you can try around with the trains. There are some debug messages that shouldn't disturb you. Note that anything may change in future releases.
|
||||
- Use the Track(placer) item to place tracks. In most cases it will adjust rails in the direction you need them.
|
||||
- use the trackworker tool to rotate tracks(right-click) and to change normal rails into switches(left-click)
|
||||
- to overcome heights you need the rails with the strange gravel texture in the background, place them and you will understand.
|
||||
- place any of the wagons in different colors by picking the item and placing it on a track.
|
||||
- right-click a wagon to sit onto it.
|
||||
- right-click a wagon while holding W / S to accelerate/decelerate the train. This will fail if the train can't move in the desired direction.
|
||||
- drive two trains together and they will connect automatically.
|
||||
- right-click a wagon while holding sneak key to split a train at this wagon
|
||||
- right-click a wagon while holding aux1 to print useful(TM) information to the console
|
||||
|
||||
Have fun!
|
|
@ -0,0 +1,24 @@
|
|||
Hi
|
||||
Since there are no trains mods out there that satisfied my needs, I decided to write my own.
|
||||
Until now are supported:
|
||||
- tracks and switches, based on angles of 45 degrees
|
||||
- wagons (atm they all look like very simple locomotives in different colors) that drive on these rails and collide with nodes in the environment(they need 3x3x3 space)
|
||||
- conecting/disconnecting of wagons/trains
|
||||
Planned features:
|
||||
- trains will only move if a locomotive is in train
|
||||
- locomotives need coal to drive (and water...)
|
||||
- more types of trains and rails(electric, diesel, maglevs...)
|
||||
- better controls
|
||||
- cool models for trains and rails
|
||||
|
||||
At the moment, you can try around with the trains. There are some debug messages that shouldn't disturb you. Note that anything may change in future releases.
|
||||
- Use the Track(placer) item to place tracks. In most cases it will adjust rails in the direction you need them.
|
||||
- use the trackworker tool to rotate tracks(right-click) and to change normal rails into switches(left-click)
|
||||
- place any of the wagons in different colors by picking the item and placing it on a track.
|
||||
- right-click a wagon to sit onto it.
|
||||
- right-click a wagon while holding W / S to accelerate/decelerate the train. This will fail if the train can't move in the desired direction.
|
||||
- drive two trains together and they will connect automatically.
|
||||
- right-click a wagon while holding sneak key to split a train at this wagon
|
||||
- right-click a wagon while holding aux1 to print useful(TM) information to the console
|
||||
|
||||
Have fun!
|
After Width: | Height: | Size: 1.1 MiB |
After Width: | Height: | Size: 328 B |
After Width: | Height: | Size: 1.2 MiB |
After Width: | Height: | Size: 908 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 328 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 527 B |
After Width: | Height: | Size: 552 B |
After Width: | Height: | Size: 414 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 526 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 491 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 621 B |
After Width: | Height: | Size: 621 B |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 1.2 KiB |
|
@ -0,0 +1,200 @@
|
|||
--trackplacer.lua
|
||||
--holds code for the track-placing system. the default 'track' item will be a craftitem that places rails as needed. this will neither place or change switches nor place vertical rails.
|
||||
|
||||
--keys:conn1_conn2 (example:1_4)
|
||||
--values:{name=x, param2=x}
|
||||
advtrains.trackplacer_dir_to_node_mapping={}
|
||||
--keys are nodenames!
|
||||
advtrains.trackplacer_modified_rails={}
|
||||
|
||||
function advtrains.trackplacer_register(nodename, conn1, conn2)
|
||||
for i=0,3 do
|
||||
advtrains.trackplacer_dir_to_node_mapping[((conn1+2*i)%8).."_"..((conn2+2*i)%8)]={name=nodename, param2=i}
|
||||
advtrains.trackplacer_dir_to_node_mapping[((conn2+2*i)%8).."_"..((conn1+2*i)%8)]={name=nodename, param2=i}
|
||||
end
|
||||
advtrains.trackplacer_modified_rails[nodename]=true
|
||||
end
|
||||
function advtrains.find_adjacent_tracks(pos)--TODO vertical calculations(check node below)
|
||||
local conn1=0
|
||||
while conn1<8 and not advtrains.is_track_and_drives_on(minetest.get_node(advtrains.dirCoordSet(pos, conn1)).name, advtrains.all_tracktypes) do
|
||||
conn1=conn1+1
|
||||
end
|
||||
if conn1>=8 then
|
||||
return nil, nil
|
||||
end
|
||||
local conn2=0
|
||||
while conn2<8 and not advtrains.is_track_and_drives_on(minetest.get_node(advtrains.dirCoordSet(pos, conn2)).name, advtrains.all_tracktypes) or conn2==conn1 do
|
||||
conn2=conn2+1
|
||||
end
|
||||
if conn2>=8 then
|
||||
return conn1, nil
|
||||
end
|
||||
return conn1, conn2
|
||||
end
|
||||
function advtrains.placetrack(pos)
|
||||
local conn1, conn2=advtrains.find_adjacent_tracks(pos)
|
||||
|
||||
if not conn1 and not conn2 then
|
||||
minetest.set_node(pos, {name="advtrains:track_st"})
|
||||
elseif conn1 and not conn2 then
|
||||
local node1=minetest.get_node(advtrains.dirCoordSet(pos, conn1))
|
||||
local node1_conn1, node1_conn2=advtrains.get_track_connections(node1.name, node1.param2)
|
||||
local node1_backconnects=(conn1+4)%8==node1_conn1 or (conn1+4)%8==node1_conn2
|
||||
|
||||
if not node1_backconnects and advtrains.trackplacer_modified_rails[node1.name] then
|
||||
--check if this rail has a dangling connection
|
||||
--TODO possible problems on |- situation
|
||||
if not advtrains.is_track_and_drives_on(minetest.get_node(advtrains.dirCoordSet(pos, node1_conn1)).name, advtrains.all_tracktypes) then
|
||||
if advtrains.trackplacer_dir_to_node_mapping[node1_conn1.."_"..((conn1+4)%8)] then
|
||||
minetest.set_node(advtrains.dirCoordSet(pos, conn1), advtrains.trackplacer_dir_to_node_mapping[node1_conn1.."_"..((conn1+4)%8)])
|
||||
end
|
||||
elseif not advtrains.is_track_and_drives_on(minetest.get_node(advtrains.dirCoordSet(pos, node1_conn2)).name, advtrains.all_tracktypes) then
|
||||
if advtrains.trackplacer_dir_to_node_mapping[node1_conn2.."_"..((conn1+4)%8)] then
|
||||
minetest.set_node(advtrains.dirCoordSet(pos, conn1), advtrains.trackplacer_dir_to_node_mapping[node1_conn2.."_"..((conn1+4)%8)])
|
||||
end
|
||||
end
|
||||
end
|
||||
--second end will be free. place standard rail
|
||||
if conn1%2==1 then
|
||||
minetest.set_node(pos, {name="advtrains:track_st_45", param2=(conn1-1)/2})
|
||||
else
|
||||
minetest.set_node(pos, {name="advtrains:track_st", param2=conn1/2})
|
||||
end
|
||||
elseif conn1 and conn2 then
|
||||
if not advtrains.trackplacer_dir_to_node_mapping[conn1.."_"..conn2] then
|
||||
minetest.set_node(pos, {name="advtrains:track_st"})
|
||||
return
|
||||
end
|
||||
local node1=minetest.get_node(advtrains.dirCoordSet(pos, conn1))
|
||||
local node1_conn1, node1_conn2=advtrains.get_track_connections(node1.name, node1.param2)
|
||||
local node1_backconnects=(conn1+4)%8==node1_conn1 or (conn1+4)%8==node1_conn2
|
||||
if not node1_backconnects and advtrains.trackplacer_modified_rails[node1.name] then
|
||||
--check if this rail has a dangling connection
|
||||
--TODO possible problems on |- situation
|
||||
if not advtrains.is_track_and_drives_on(minetest.get_node(advtrains.dirCoordSet(pos, node1_conn1)).name, advtrains.all_tracktypes) then
|
||||
if advtrains.trackplacer_dir_to_node_mapping[node1_conn1.."_"..((conn1+4)%8)] then
|
||||
minetest.set_node(advtrains.dirCoordSet(pos, conn1), advtrains.trackplacer_dir_to_node_mapping[node1_conn1.."_"..((conn1+4)%8)])
|
||||
end
|
||||
elseif not advtrains.is_track_and_drives_on(minetest.get_node(advtrains.dirCoordSet(pos, node1_conn2)).name, advtrains.all_tracktypes) then
|
||||
if advtrains.trackplacer_dir_to_node_mapping[node1_conn2.."_"..((conn1+4)%8)] then
|
||||
minetest.set_node(advtrains.dirCoordSet(pos, conn1), advtrains.trackplacer_dir_to_node_mapping[node1_conn2.."_"..((conn1+4)%8)])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local node2=minetest.get_node(advtrains.dirCoordSet(pos, conn2))
|
||||
local node2_conn1, node2_conn2=advtrains.get_track_connections(node2.name, node2.param2)
|
||||
local node2_backconnects=(conn2+4)%8==node2_conn1 or (conn2+4)%8==node2_conn2
|
||||
if not node2_backconnects and advtrains.trackplacer_modified_rails[node2.name] then
|
||||
--check if this rail has a dangling connection
|
||||
--TODO possible problems on |- situation
|
||||
if not advtrains.is_track_and_drives_on(minetest.get_node(advtrains.dirCoordSet(pos, node2_conn1)).name, advtrains.all_tracktypes) then
|
||||
if advtrains.trackplacer_dir_to_node_mapping[node2_conn1.."_"..((conn2+4)%8)] then
|
||||
minetest.set_node(advtrains.dirCoordSet(pos, conn2), advtrains.trackplacer_dir_to_node_mapping[node2_conn1.."_"..((conn2+4)%8)])
|
||||
end
|
||||
elseif not advtrains.is_track_and_drives_on(minetest.get_node(advtrains.dirCoordSet(pos, node2_conn2)).name, advtrains.all_tracktypes) then
|
||||
if advtrains.trackplacer_dir_to_node_mapping[node2_conn2.."_"..((conn1+4)%8)] then
|
||||
minetest.set_node(advtrains.dirCoordSet(pos, conn2), advtrains.trackplacer_dir_to_node_mapping[node2_conn2.."_"..((conn2+4)%8)])
|
||||
end
|
||||
end
|
||||
end
|
||||
minetest.set_node(pos, advtrains.trackplacer_dir_to_node_mapping[conn1.."_"..conn2])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
advtrains.trackworker_cycle_nodes={
|
||||
["swr_cr"]="st",
|
||||
["swr_st"]="st",
|
||||
["st"]="cr",
|
||||
["cr"]="swl_st",
|
||||
["swl_cr"]="swr_cr",
|
||||
["swl_st"]="swr_st",
|
||||
}
|
||||
|
||||
function advtrains.register_track_placer(nnprefix, imgprefix, dispname)
|
||||
minetest.register_craftitem(nnprefix.."_placer",{
|
||||
description = dispname,
|
||||
inventory_image = imgprefix.."_placer.png",
|
||||
wield_image = imgprefix.."_placer.png",
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
if pointed_thing.type=="node" then
|
||||
local pos=pointed_thing.above
|
||||
if minetest.registered_nodes[minetest.get_node(pos).name] and minetest.registered_nodes[minetest.get_node(pos).name].buildable_to then
|
||||
advtrains.placetrack(pos, nnprefix)
|
||||
itemstack:take_item()
|
||||
end
|
||||
end
|
||||
return itemstack
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
|
||||
minetest.register_craftitem("advtrains:trackworker",{
|
||||
description = "Track Worker Tool\n\nLeft-click: change rail type (straight/curve/switch)\nRight-click: rotate rail",
|
||||
groups = {cracky=1}, -- key=name, value=rating; rating=1..3.
|
||||
inventory_image = "advtrains_trackworker.png",
|
||||
wield_image = "advtrains_trackworker.png",
|
||||
stack_max = 1,
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
if pointed_thing.type=="node" then
|
||||
local pos=pointed_thing.under
|
||||
local node=minetest.get_node(pos)
|
||||
|
||||
if not advtrains.is_track_and_drives_on(minetest.get_node(pos).name, advtrains.all_tracktypes) then return end
|
||||
if advtrains.is_train_at_pos(pos) then return end
|
||||
local nodeprefix, railtype=string.match(node.name, "^(.-)_(.+)$")
|
||||
|
||||
local basename=string.match(railtype, "^(.+)_45$")
|
||||
if basename then
|
||||
if not advtrains.trackworker_cycle_nodes[basename] then
|
||||
print("[advtrains]rail not workable by trackworker")
|
||||
return
|
||||
end
|
||||
minetest.set_node(pos, {name=nodeprefix.."_"..basename, param2=(node.param2+1)%4})
|
||||
return
|
||||
else
|
||||
if not advtrains.trackworker_cycle_nodes[railtype] then
|
||||
print("[advtrains]rail not workable by trackworker")
|
||||
return
|
||||
end
|
||||
minetest.set_node(pos, {name=nodeprefix.."_"..railtype.."_45", param2=node.param2})
|
||||
end
|
||||
end
|
||||
end,
|
||||
on_use=function(itemstack, user, pointed_thing)
|
||||
if pointed_thing.type=="node" then
|
||||
local pos=pointed_thing.under
|
||||
local node=minetest.get_node(pos)
|
||||
|
||||
if not advtrains.is_track_and_drives_on(minetest.get_node(pos).name, advtrains.all_tracktypes) then return end
|
||||
if advtrains.is_train_at_pos(pos) then return end
|
||||
local nodeprefix, railtype=string.match(node.name, "^(.-)_(.+)$")
|
||||
|
||||
local basename=string.match(railtype, "^(.+)_45$")
|
||||
if basename then
|
||||
if not advtrains.trackworker_cycle_nodes[basename] then
|
||||
print("[advtrains]trackworker does not know what to set here...")
|
||||
return
|
||||
end
|
||||
print(advtrains.trackworker_cycle_nodes[basename].."_45")
|
||||
minetest.set_node(pos, {name=nodeprefix.."_"..advtrains.trackworker_cycle_nodes[basename].."_45", param2=node.param2})
|
||||
return
|
||||
else
|
||||
if not advtrains.trackworker_cycle_nodes[railtype] then
|
||||
print("[advtrains]trackworker does not know what to set here...")
|
||||
return
|
||||
end
|
||||
minetest.set_node(pos, {name=nodeprefix.."_"..advtrains.trackworker_cycle_nodes[railtype], param2=node.param2})
|
||||
end
|
||||
--invalidate trains
|
||||
for k,v in pairs(advtrains.trains) do
|
||||
v.restore_add_index=v.index-math.floor(v.index+0.5)
|
||||
v.path=nil
|
||||
v.index=nil
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
|
@ -0,0 +1,200 @@
|
|||
--trackplacer.lua
|
||||
--holds code for the track-placing system. the default 'track' item will be a craftitem that places rails as needed. this will neither place or change switches nor place vertical rails.
|
||||
|
||||
--keys:conn1_conn2 (example:1_4)
|
||||
--values:{name=x, param2=x}
|
||||
advtrains.trackplacer_dir_to_node_mapping={}
|
||||
--keys are nodenames!
|
||||
advtrains.trackplacer_modified_rails={}
|
||||
|
||||
function advtrains.trackplacer_register(nodename, conn1, conn2)
|
||||
for i=0,3 do
|
||||
advtrains.trackplacer_dir_to_node_mapping[((conn1+2*i)%8).."_"..((conn2+2*i)%8)]={name=nodename, param2=i}
|
||||
advtrains.trackplacer_dir_to_node_mapping[((conn2+2*i)%8).."_"..((conn1+2*i)%8)]={name=nodename, param2=i}
|
||||
end
|
||||
advtrains.trackplacer_modified_rails[nodename]=true
|
||||
end
|
||||
function advtrains.find_adjacent_tracks(pos)--TODO vertical calculations(check node below)
|
||||
local conn1=0
|
||||
while conn1<8 and not advtrains.is_track_and_drives_on(minetest.get_node(advtrains.dirCoordSet(pos, conn1)).name, advtrains.all_tracktypes) do
|
||||
conn1=conn1+1
|
||||
end
|
||||
if conn1>=8 then
|
||||
return nil, nil
|
||||
end
|
||||
local conn2=0
|
||||
while conn2<8 and not advtrains.is_track_and_drives_on(minetest.get_node(advtrains.dirCoordSet(pos, conn2)).name, advtrains.all_tracktypes) or conn2==conn1 do
|
||||
conn2=conn2+1
|
||||
end
|
||||
if conn2>=8 then
|
||||
return conn1, nil
|
||||
end
|
||||
return conn1, conn2
|
||||
end
|
||||
function advtrains.placetrack(pos)
|
||||
local conn1, conn2=advtrains.find_adjacent_tracks(pos)
|
||||
|
||||
if not conn1 and not conn2 then
|
||||
minetest.set_node(pos, {name="advtrains:track_st"})
|
||||
elseif conn1 and not conn2 then
|
||||
local node1=minetest.get_node(advtrains.dirCoordSet(pos, conn1))
|
||||
local node1_conn1, node1_conn2=advtrains.get_track_connections(node1.name, node1.param2)
|
||||
local node1_backconnects=(conn1+4)%8==node1_conn1 or (conn1+4)%8==node1_conn2
|
||||
|
||||
if not node1_backconnects and advtrains.trackplacer_modified_rails[node1.name] then
|
||||
--check if this rail has a dangling connection
|
||||
--TODO possible problems on |- situation
|
||||
if not advtrains.is_track_and_drives_on(minetest.get_node(advtrains.dirCoordSet(pos, node1_conn1)).name, advtrains.all_tracktypes) then
|
||||
if advtrains.trackplacer_dir_to_node_mapping[node1_conn1.."_"..((conn1+4)%8)] then
|
||||
minetest.set_node(advtrains.dirCoordSet(pos, conn1), advtrains.trackplacer_dir_to_node_mapping[node1_conn1.."_"..((conn1+4)%8)])
|
||||
end
|
||||
elseif not advtrains.is_track_and_drives_on(minetest.get_node(advtrains.dirCoordSet(pos, node1_conn2)).name, advtrains.all_tracktypes) then
|
||||
if advtrains.trackplacer_dir_to_node_mapping[node1_conn2.."_"..((conn1+4)%8)] then
|
||||
minetest.set_node(advtrains.dirCoordSet(pos, conn1), advtrains.trackplacer_dir_to_node_mapping[node1_conn2.."_"..((conn1+4)%8)])
|
||||
end
|
||||
end
|
||||
end
|
||||
--second end will be free. place standard rail
|
||||
if conn1%2==1 then
|
||||
minetest.set_node(pos, {name="advtrains:track_st_45", param2=(conn1-1)/2})
|
||||
else
|
||||
minetest.set_node(pos, {name="advtrains:track_st", param2=conn1/2})
|
||||
end
|
||||
elseif conn1 and conn2 then
|
||||
if not advtrains.trackplacer_dir_to_node_mapping[conn1.."_"..conn2] then
|
||||
minetest.set_node(pos, {name="advtrains:track_st"})
|
||||
return
|
||||
end
|
||||
local node1=minetest.get_node(advtrains.dirCoordSet(pos, conn1))
|
||||
local node1_conn1, node1_conn2=advtrains.get_track_connections(node1.name, node1.param2)
|
||||
local node1_backconnects=(conn1+4)%8==node1_conn1 or (conn1+4)%8==node1_conn2
|
||||
if not node1_backconnects and advtrains.trackplacer_modified_rails[node1.name] then
|
||||
--check if this rail has a dangling connection
|
||||
--TODO possible problems on |- situation
|
||||
if not advtrains.is_track_and_drives_on(minetest.get_node(advtrains.dirCoordSet(pos, node1_conn1)).name, advtrains.all_tracktypes) then
|
||||
if advtrains.trackplacer_dir_to_node_mapping[node1_conn1.."_"..((conn1+4)%8)] then
|
||||
minetest.set_node(advtrains.dirCoordSet(pos, conn1), advtrains.trackplacer_dir_to_node_mapping[node1_conn1.."_"..((conn1+4)%8)])
|
||||
end
|
||||
elseif not advtrains.is_track_and_drives_on(minetest.get_node(advtrains.dirCoordSet(pos, node1_conn2)).name, advtrains.all_tracktypes) then
|
||||
if advtrains.trackplacer_dir_to_node_mapping[node1_conn2.."_"..((conn1+4)%8)] then
|
||||
minetest.set_node(advtrains.dirCoordSet(pos, conn1), advtrains.trackplacer_dir_to_node_mapping[node1_conn2.."_"..((conn1+4)%8)])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local node2=minetest.get_node(advtrains.dirCoordSet(pos, conn2))
|
||||
local node2_conn1, node2_conn2=advtrains.get_track_connections(node2.name, node2.param2)
|
||||
local node2_backconnects=(conn2+4)%8==node2_conn1 or (conn2+4)%8==node2_conn2
|
||||
if not node2_backconnects and advtrains.trackplacer_modified_rails[node2.name] then
|
||||
--check if this rail has a dangling connection
|
||||
--TODO possible problems on |- situation
|
||||
if not advtrains.is_track_and_drives_on(minetest.get_node(advtrains.dirCoordSet(pos, node2_conn1)).name, advtrains.all_tracktypes) then
|
||||
if advtrains.trackplacer_dir_to_node_mapping[node2_conn1.."_"..((conn2+4)%8)] then
|
||||
minetest.set_node(advtrains.dirCoordSet(pos, conn2), advtrains.trackplacer_dir_to_node_mapping[node2_conn1.."_"..((conn2+4)%8)])
|
||||
end
|
||||
elseif not advtrains.is_track_and_drives_on(minetest.get_node(advtrains.dirCoordSet(pos, node2_conn2)).name, advtrains.all_tracktypes) then
|
||||
if advtrains.trackplacer_dir_to_node_mapping[node2_conn2.."_"..((conn1+4)%8)] then
|
||||
minetest.set_node(advtrains.dirCoordSet(pos, conn2), advtrains.trackplacer_dir_to_node_mapping[node2_conn2.."_"..((conn2+4)%8)])
|
||||
end
|
||||
end
|
||||
end
|
||||
minetest.set_node(pos, advtrains.trackplacer_dir_to_node_mapping[conn1.."_"..conn2])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
advtrains.trackworker_cycle_nodes={
|
||||
["swr_cr"]="st",
|
||||
["swr_st"]="st",
|
||||
["st"]="cr",
|
||||
["cr"]="swl_st",
|
||||
["swl_cr"]="swr_cr",
|
||||
["swl_st"]="swr_st",
|
||||
}
|
||||
|
||||
function advtrains.register_track_placer(nnprefix, imgprefix, dispname)
|
||||
minetest.register_craftitem(nnprefix.."_placer",{
|
||||
description = dispname,
|
||||
inventory_image = imgprefix.."_placer.png",
|
||||
wield_image = imgprefix.."_placer.png",
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
if pointed_thing.type=="node" then
|
||||
local pos=pointed_thing.above
|
||||
if minetest.registered_nodes[minetest.get_node(pos).name] and minetest.registered_nodes[minetest.get_node(pos).name].buildable_to then
|
||||
advtrains.placetrack(pos, nnprefix)
|
||||
itemstack:take_item()
|
||||
end
|
||||
end
|
||||
return itemstack
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
|
||||
minetest.register_craftitem("advtrains:trackworker",{
|
||||
description = "Track Worker Tool\n\nLeft-click: change rail type (straight/curve/switch)\nRight-click: rotate rail",
|
||||
groups = {cracky=1}, -- key=name, value=rating; rating=1..3.
|
||||
inventory_image = "advtrains_trackworker.png",
|
||||
wield_image = "advtrains_trackworker.png",
|
||||
stack_max = 1,
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
if pointed_thing.type=="node" then
|
||||
local pos=pointed_thing.under
|
||||
local node=minetest.get_node(pos)
|
||||
|
||||
if not advtrains.is_track_and_drives_on(minetest.get_node(pos).name, advtrains.all_tracktypes) then return end
|
||||
if advtrains.is_train_at_pos(pos) then return end
|
||||
local nodeprefix, railtype=string.match(node.name, "^(.-)_(.+)$")
|
||||
|
||||
local basename=string.match(railtype, "^(.+)_45$")
|
||||
if basename then
|
||||
if not advtrains.trackworker_cycle_nodes[basename] then
|
||||
print("[advtrains]rail not workable by trackworker")
|
||||
return
|
||||
end
|
||||
minetest.set_node(pos, {name=nodeprefix.."_"..basename, param2=(node.param2+1)%4})
|
||||
return
|
||||
else
|
||||
if not advtrains.trackworker_cycle_nodes[railtype] then
|
||||
print("[advtrains]rail not workable by trackworker")
|
||||
return
|
||||
end
|
||||
minetest.set_node(pos, {name=nodeprefix.."_"..railtype.."_45", param2=node.param2})
|
||||
end
|
||||
end
|
||||
end,
|
||||
on_use=function(itemstack, user, pointed_thing)
|
||||
if pointed_thing.type=="node" then
|
||||
local pos=pointed_thing.under
|
||||
local node=minetest.get_node(pos)
|
||||
|
||||
if not advtrains.is_track_and_drives_on(minetest.get_node(pos).name, advtrains.all_tracktypes) then return end
|
||||
if advtrains.is_train_at_pos(pos) then return end
|
||||
local nodeprefix, railtype=string.match(node.name, "^(.-)_(.+)$")
|
||||
|
||||
local basename=string.match(railtype, "^(.+)_45$")
|
||||
if basename then
|
||||
if not advtrains.trackworker_cycle_nodes[basename] then
|
||||
print("[advtrains]trackworker does not know what to set here...")
|
||||
return
|
||||
end
|
||||
print(advtrains.trackworker_cycle_nodes[basename].."_45")
|
||||
minetest.set_node(pos, {name=nodeprefix.."_"..advtrains.trackworker_cycle_nodes[basename].."_45", param2=node.param2})
|
||||
return
|
||||
else
|
||||
if not advtrains.trackworker_cycle_nodes[railtype] then
|
||||
print("[advtrains]trackworker does not know what to set here...")
|
||||
return
|
||||
end
|
||||
minetest.set_node(pos, {name=nodeprefix.."_"..advtrains.trackworker_cycle_nodes[railtype], param2=node.param2})
|
||||
end
|
||||
--invalidate trains
|
||||
for k,v in pairs(advtrains.trains) do
|
||||
v.restore_add_index=v.index-math.floor(v.index+0.5)
|
||||
v.path=nil
|
||||
v.index=nil
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
|
@ -0,0 +1,152 @@
|
|||
--advtrains by orwell96, see readme.txt
|
||||
|
||||
--[[TracksDefinition
|
||||
nodename_prefix
|
||||
texture_prefix
|
||||
description
|
||||
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={}
|
||||
|
||||
function advtrains.register_tracks(tracktype, def)
|
||||
local function make_switchfunc(suffix_target)
|
||||
return function(pos, node)
|
||||
if advtrains.is_train_at_pos(pos) then return end
|
||||
advtrains.invalidate_all_paths()
|
||||
minetest.set_node(pos, {name=def.nodename_prefix.."_"..suffix_target, param2=node.param2})
|
||||
end
|
||||
end
|
||||
local function make_overdef(img_suffix, conn1, conn2, switchfunc)
|
||||
return {
|
||||
tiles = {def.texture_prefix.."_"..img_suffix..".png"},
|
||||
inventory_image = def.texture_prefix.."_"..img_suffix..".png",
|
||||
wield_image = def.texture_prefix.."_"..img_suffix..".png",
|
||||
connect1=conn1,
|
||||
connect2=conn2,
|
||||
on_rightclick=switchfunc,
|
||||
}
|
||||
end
|
||||
local common_def={
|
||||
description = def.description,
|
||||
drawtype = "mesh",
|
||||
mesh = "trackplane.b3d",
|
||||
paramtype="light",
|
||||
paramtype2="facedir",
|
||||
walkable = false,
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
|
||||
},
|
||||
groups = {
|
||||
attached_node=1,
|
||||
["advtrains_track_"..tracktype]=1,
|
||||
dig_immediate=2,
|
||||
not_in_creative_inventory=1,
|
||||
},
|
||||
rely1=0,
|
||||
rely2=0,
|
||||
railheight=0,
|
||||
drop="advtrains:placetrack_"..tracktype,
|
||||
--on_rightclick=function(pos, node, clicker)
|
||||
-- minetest.set_node(pos, {name=node.name, param2=(node.param2+1)%4})
|
||||
--end
|
||||
can_dig=function(pos)
|
||||
return not advtrains.is_train_at_pos(pos)
|
||||
end,
|
||||
after_dig_node=function()
|
||||
advtrains.invalidate_all_paths()
|
||||
end
|
||||
}
|
||||
minetest.register_node(def.nodename_prefix.."_st", advtrains.merge_tables(common_def, make_overdef("st", 0, 4), def.straight or {}))
|
||||
minetest.register_node(def.nodename_prefix.."_st_45", advtrains.merge_tables(common_def, make_overdef("st_45", 1, 5), def.straight45 or {}))
|
||||
|
||||
minetest.register_node(def.nodename_prefix.."_cr", advtrains.merge_tables(common_def, make_overdef("cr", 0, 3), def.curve or {}))
|
||||
minetest.register_node(def.nodename_prefix.."_cr_45", advtrains.merge_tables(common_def, make_overdef("cr_45", 1, 4), def.curve45 or {}))
|
||||
|
||||
advtrains.trackplacer_register(def.nodename_prefix.."_st", 0, 4)
|
||||
advtrains.trackplacer_register(def.nodename_prefix.."_st_45", 1, 5)
|
||||
advtrains.trackplacer_register(def.nodename_prefix.."_cr", 0, 3)
|
||||
advtrains.trackplacer_register(def.nodename_prefix.."_cr_45", 1, 4)
|
||||
|
||||
|
||||
minetest.register_node(def.nodename_prefix.."_swl_st", advtrains.merge_tables(common_def, make_overdef("swl_st", 0, 4, make_switchfunc("swl_cr")), def.lswitchst or {}))
|
||||
minetest.register_node(def.nodename_prefix.."_swl_st_45", advtrains.merge_tables(common_def, make_overdef("swl_st_45", 1, 5, make_switchfunc("swl_cr_45")), def.lswitchst45 or {}))
|
||||
minetest.register_node(def.nodename_prefix.."_swl_cr", advtrains.merge_tables(common_def, make_overdef("swl_cr", 0, 3, make_switchfunc("swl_st")), def.lswitchcr or {}))
|
||||
minetest.register_node(def.nodename_prefix.."_swl_cr_45", advtrains.merge_tables(common_def, make_overdef("swl_cr_45", 1, 4, make_switchfunc("swl_st_45")), def.lswitchcr45 or {}))
|
||||
|
||||
minetest.register_node(def.nodename_prefix.."_swr_st", advtrains.merge_tables(common_def, make_overdef("swr_st", 0, 4, make_switchfunc("swr_cr")), def.rswitchst or {}))
|
||||
minetest.register_node(def.nodename_prefix.."_swr_st_45", advtrains.merge_tables(common_def, make_overdef("swr_st_45", 1, 5, make_switchfunc("swr_cr_45")), def.rswitchst45 or {}))
|
||||
minetest.register_node(def.nodename_prefix.."_swr_cr", advtrains.merge_tables(common_def, make_overdef("swr_cr", 0, 5, make_switchfunc("swr_st")), def.rswitchcr or {}))
|
||||
minetest.register_node(def.nodename_prefix.."_swr_cr_45", advtrains.merge_tables(common_def, make_overdef("swr_cr_45", 1, 6, make_switchfunc("swr_st_45")), def.rswitchcr45 or {}))
|
||||
|
||||
minetest.register_node(def.nodename_prefix.."_vert1", advtrains.merge_tables(common_def, make_overdef("vert1", 0, 4), {
|
||||
mesh = "trackvertical1.b3d",
|
||||
rely1=0,
|
||||
rely2=0.5,
|
||||
railheight=0.25,
|
||||
description = def.description.." (vertical track lower node)",
|
||||
}, def.vert1 or {}))
|
||||
minetest.register_node(def.nodename_prefix.."_vert2", advtrains.merge_tables(common_def, make_overdef("vert2", 0, 4), {
|
||||
mesh = "trackvertical2.b3d",
|
||||
rely1=0.5,
|
||||
rely2=1,
|
||||
railheight=0.75,
|
||||
description = def.description.." (vertical track lower node)",
|
||||
},def.vert2 or {}))
|
||||
|
||||
advtrains.register_track_placer(def.nodename_prefix, def.texture_prefix, def.description)
|
||||
table.insert(advtrains.all_tracktypes, tracktype)
|
||||
end
|
||||
|
||||
advtrains.register_tracks("regular", {
|
||||
nodename_prefix="advtrains:track",
|
||||
texture_prefix="track",
|
||||
description="Regular Train Track",
|
||||
})
|
||||
|
||||
function advtrains.is_track_and_drives_on(nodename, drives_on)
|
||||
if not minetest.registered_nodes[nodename] then
|
||||
return false
|
||||
end
|
||||
local nodedef=minetest.registered_nodes[nodename]
|
||||
for k,v in ipairs(drives_on) do
|
||||
if nodedef.groups["advtrains_track_"..v] then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function advtrains.get_track_connections(name, param2)
|
||||
local nodedef=minetest.registered_nodes[name]
|
||||
if not nodedef then print("[advtrains] get_track_connections couldn't find nodedef for nodename "..(name or "nil")) return 0,4 end
|
||||
local noderot=param2
|
||||
if not param2 then noderot=0 end
|
||||
if noderot > 3 then print("[advtrains] get_track_connections: rail has invaild param2 of "..noderot) noderot=0 end
|
||||
|
||||
return (nodedef.connect1 + 2 * noderot)%8, (nodedef.connect2 + 2 * noderot)%8, nodedef.rely1 or 0, nodedef.rely2 or 0, nodedef.railheight or 0
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
--advtrains by orwell96, see readme.txt
|
||||
|
||||
--[[TracksDefinition
|
||||
nodename_prefix
|
||||
texture_prefix
|
||||
description
|
||||
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={}
|
||||
|
||||
function advtrains.register_tracks(tracktype, def)
|
||||
local function make_switchfunc(suffix_target)
|
||||
return function(pos, node)
|
||||
if advtrains.is_train_at_pos(pos) then return end
|
||||
advtrains.invalidate_all_paths()
|
||||
minetest.set_node(pos, {name=def.nodename_prefix.."_"..suffix_target, param2=node.param2})
|
||||
end
|
||||
end
|
||||
local function make_overdef(img_suffix, conn1, conn2, switchfunc)
|
||||
return {
|
||||
tiles = {def.texture_prefix.."_"..img_suffix..".png"},
|
||||
inventory_image = def.texture_prefix.."_"..img_suffix..".png",
|
||||
wield_image = def.texture_prefix.."_"..img_suffix..".png",
|
||||
connect1=conn1,
|
||||
connect2=conn2,
|
||||
on_rightclick=switchfunc,
|
||||
}
|
||||
end
|
||||
local common_def={
|
||||
description = def.description,
|
||||
drawtype = "mesh",
|
||||
mesh = "trackplane.b3d",
|
||||
paramtype="light",
|
||||
paramtype2="facedir",
|
||||
walkable = false,
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
|
||||
},
|
||||
groups = {
|
||||
attached_node=1,
|
||||
["advtrains_track_"..tracktype]=1,
|
||||
dig_immediate=2,
|
||||
not_in_creative_inventory=1,
|
||||
},
|
||||
rely1=0,
|
||||
rely2=0,
|
||||
railheight=0,
|
||||
drop="advtrains:placetrack_"..tracktype,
|
||||
--on_rightclick=function(pos, node, clicker)
|
||||
-- minetest.set_node(pos, {name=node.name, param2=(node.param2+1)%4})
|
||||
--end
|
||||
can_dig=function(pos)
|
||||
return not advtrains.is_train_at_pos(pos)
|
||||
end,
|
||||
after_dig_node=function()
|
||||
advtrains.invalidate_all_paths()
|
||||
end
|
||||
}
|
||||
minetest.register_node(def.nodename_prefix.."_st", advtrains.merge_tables(common_def, make_overdef("st", 0, 4), def.straight or {}))
|
||||
minetest.register_node(def.nodename_prefix.."_st_45", advtrains.merge_tables(common_def, make_overdef("st_45", 1, 5), def.straight45 or {}))
|
||||
|
||||
minetest.register_node(def.nodename_prefix.."_cr", advtrains.merge_tables(common_def, make_overdef("cr", 0, 3), def.curve or {}))
|
||||
minetest.register_node(def.nodename_prefix.."_cr_45", advtrains.merge_tables(common_def, make_overdef("cr_45", 1, 4), def.curve45 or {}))
|
||||
|
||||
advtrains.trackplacer_register(def.nodename_prefix.."_st", 0, 4)
|
||||
advtrains.trackplacer_register(def.nodename_prefix.."_st_45", 1, 5)
|
||||
advtrains.trackplacer_register(def.nodename_prefix.."_cr", 0, 3)
|
||||
advtrains.trackplacer_register(def.nodename_prefix.."_cr_45", 1, 4)
|
||||
|
||||
|
||||
minetest.register_node(def.nodename_prefix.."_swl_st", advtrains.merge_tables(common_def, make_overdef("swl_st", 0, 4, make_switchfunc("swl_cr")), def.lswitchst or {}))
|
||||
minetest.register_node(def.nodename_prefix.."_swl_st_45", advtrains.merge_tables(common_def, make_overdef("swl_st_45", 1, 5, make_switchfunc("swl_cr_45")), def.lswitchst45 or {}))
|
||||
minetest.register_node(def.nodename_prefix.."_swl_cr", advtrains.merge_tables(common_def, make_overdef("swl_cr", 0, 3, make_switchfunc("swl_st")), def.lswitchcr or {}))
|
||||
minetest.register_node(def.nodename_prefix.."_swl_cr_45", advtrains.merge_tables(common_def, make_overdef("swl_cr_45", 1, 4, make_switchfunc("swl_st_45")), def.lswitchcr45 or {}))
|
||||
|
||||
minetest.register_node(def.nodename_prefix.."_swr_st", advtrains.merge_tables(common_def, make_overdef("swr_st", 0, 4, make_switchfunc("swr_cr")), def.rswitchst or {}))
|
||||
minetest.register_node(def.nodename_prefix.."_swr_st_45", advtrains.merge_tables(common_def, make_overdef("swr_st_45", 1, 5, make_switchfunc("swr_cr_45")), def.rswitchst45 or {}))
|
||||
minetest.register_node(def.nodename_prefix.."_swr_cr", advtrains.merge_tables(common_def, make_overdef("swr_cr", 0, 5, make_switchfunc("swr_st")), def.rswitchcr or {}))
|
||||
minetest.register_node(def.nodename_prefix.."_swr_cr_45", advtrains.merge_tables(common_def, make_overdef("swr_cr_45", 1, 6, make_switchfunc("swr_st_45")), def.rswitchcr45 or {}))
|
||||
|
||||
minetest.register_node(def.nodename_prefix.."_vert1", advtrains.merge_tables(common_def, make_overdef("vert1", 0, 4), {
|
||||
mesh = "trackvertical1.b3d",
|
||||
rely1=0,
|
||||
rely2=0.5,
|
||||
railheight=0.25,
|
||||
}, def.straight or {}))
|
||||
minetest.register_node(def.nodename_prefix.."_vert2", advtrains.merge_tables(common_def, make_overdef("vert2", 0, 4), {
|
||||
mesh = "trackvertical2.b3d",
|
||||
rely1=0.5,
|
||||
rely2=1,
|
||||
railheight=0.75,
|
||||
},def.straight45 or {}))
|
||||
|
||||
advtrains.register_track_placer(def.nodename_prefix, def.texture_prefix, def.description)
|
||||
table.insert(advtrains.all_tracktypes, tracktype)
|
||||
end
|
||||
|
||||
advtrains.register_tracks("regular", {
|
||||
nodename_prefix="advtrains:track",
|
||||
texture_prefix="track",
|
||||
description="Regular Train Track",
|
||||
})
|
||||
|
||||
function advtrains.is_track_and_drives_on(nodename, drives_on)
|
||||
if not minetest.registered_nodes[nodename] then
|
||||
return false
|
||||
end
|
||||
local nodedef=minetest.registered_nodes[nodename]
|
||||
for k,v in ipairs(drives_on) do
|
||||
if nodedef.groups["advtrains_track_"..v] then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function advtrains.get_track_connections(name, param2)
|
||||
local nodedef=minetest.registered_nodes[name]
|
||||
if not nodedef then print("[advtrains] get_track_connections couldn't find nodedef for nodename "..(name or "nil")) return 0,4 end
|
||||
local noderot=param2
|
||||
if not param2 then noderot=0 end
|
||||
if noderot > 3 then print("[advtrains] get_track_connections: rail has invaild param2 of "..noderot) noderot=0 end
|
||||
|
||||
return (nodedef.connect1 + 2 * noderot)%8, (nodedef.connect2 + 2 * noderot)%8, nodedef.rely1 or 0, nodedef.rely2 or 0, nodedef.railheight or 0
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
|
||||
advtrains.hud = {}
|
||||
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
advtrains.hud[player:get_player_name()] = nil
|
||||
end)
|
||||
|
||||
function advtrains.set_trainhud(name, text)
|
||||
local hud = advtrains.hud[name]
|
||||
local player=minetest.get_player_by_name(name)
|
||||
if not hud then
|
||||
hud = {}
|
||||
advtrains.hud[name] = hud
|
||||
hud.id = player:hud_add({
|
||||
hud_elem_type = "text",
|
||||
name = "ADVTRAINS",
|
||||
number = 0xFFFFFF,
|
||||
position = {x=0.5, y=0.7},
|
||||
offset = {x=0, y=0},
|
||||
text = text,
|
||||
scale = {x=200, y=60},
|
||||
alignment = {x=0, y=0},
|
||||
})
|
||||
hud.oldText=text
|
||||
return
|
||||
elseif hud.oldText ~= text then
|
||||
player:hud_change(hud.id, "text", text)
|
||||
hud.oldText=text
|
||||
end
|
||||
end
|
||||
function advtrains.hud_train_format(train, flip)
|
||||
local fct=1
|
||||
if flip then fct=-1 end
|
||||
if not train then return "" end
|
||||
local max=advtrains.all_traintypes[train.traintype].max_speed or 10
|
||||
local vel=math.ceil(train.velocity)*fct
|
||||
local tvel=math.ceil(train.tarvelocity)*fct
|
||||
local firstLine, secondLine
|
||||
if vel<0 then
|
||||
firstLine="Speed: <"..string.rep("_", vel+max)..string.rep("+", -vel).."|"..string.rep("_", max)..">"
|
||||
else
|
||||
firstLine="Speed: <"..string.rep("_", max).."|"..string.rep("+", vel)..string.rep("_", max-vel)..">"
|
||||
end
|
||||
if tvel<0 then
|
||||
secondLine="Target: <"..string.rep("_", tvel+max)..string.rep("+", -tvel).."|"..string.rep("_", max)..">"
|
||||
else
|
||||
secondLine="Target: <"..string.rep("_", max).."|"..string.rep("+", tvel)..string.rep("_", max-tvel)..">"
|
||||
end
|
||||
if vel==0 then
|
||||
return firstLine.."\n"..secondLine.."\nup for forward, down for backward, use to get off train. "
|
||||
elseif vel<0 then
|
||||
return firstLine.."\n"..secondLine.."\nPress up to decelerate, down to accelerate, sneak to stop."
|
||||
elseif vel>0 then
|
||||
return firstLine.."\n"..secondLine.."\nPress up to accelerate, down to decelerate, sneak to stop."
|
||||
end
|
||||
end
|
|
@ -0,0 +1,54 @@
|
|||
|
||||
advtrains.hud = {}
|
||||
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
advtrains.hud[player:get_player_name()] = nil
|
||||
end)
|
||||
|
||||
function advtrains.set_trainhud(name, text)
|
||||
local hud = advtrains.hud[name]
|
||||
local player=minetest.get_player_by_name(name)
|
||||
if not hud then
|
||||
hud = {}
|
||||
advtrains.hud[name] = hud
|
||||
hud.id = player:hud_add({
|
||||
hud_elem_type = "text",
|
||||
name = "ADVTRAINS",
|
||||
number = 0xFFFFFF,
|
||||
position = {x=0.5, y=0.7},
|
||||
offset = {x=0, y=0},
|
||||
text = text,
|
||||
scale = {x=200, y=60},
|
||||
alignment = {x=0, y=0},
|
||||
})
|
||||
hud.oldText=text
|
||||
return
|
||||
elseif hud.oldText ~= text then
|
||||
player:hud_change(hud.id, "text", text)
|
||||
hud.oldText=text
|
||||
end
|
||||
end
|
||||
function advtrains.hud_train_format(train)
|
||||
if not train then return "" end
|
||||
local max=advtrains.all_traintypes[train.traintype].max_speed or 10
|
||||
local vel=math.ceil(train.velocity)
|
||||
local tvel=math.ceil(train.tarvelocity)
|
||||
local firstLine, secondLine
|
||||
if vel<0 then
|
||||
firstLine="Speed: <"..string.rep("_", vel+max)..string.rep("+", -vel).."|"..string.rep("_", max)..">"
|
||||
else
|
||||
firstLine="Speed: <"..string.rep("_", max).."|"..string.rep("+", vel)..string.rep("_", max-vel)..">"
|
||||
end
|
||||
if tvel<0 then
|
||||
secondLine="Target: <"..string.rep("_", tvel+max)..string.rep("+", -tvel).."|"..string.rep("_", max)..">"
|
||||
else
|
||||
secondLine="Target: <"..string.rep("_", max).."|"..string.rep("+", tvel)..string.rep("_", max-tvel)..">"
|
||||
end
|
||||
if vel==0 then
|
||||
return firstLine.."\n"..secondLine.."\nup for forward, down for backward, use to get off train. "
|
||||
elseif vel<0 then
|
||||
return firstLine.."\n"..secondLine.."\nPress up to decelerate, down to accelerate, sneak to stop."
|
||||
elseif vel>0 then
|
||||
return firstLine.."\n"..secondLine.."\nPress up to accelerate, down to decelerate, sneak to stop."
|
||||
end
|
||||
end
|
|
@ -0,0 +1,543 @@
|
|||
--trainlogic.lua
|
||||
--controls train entities stuff about connecting/disconnecting/colliding trains and other things
|
||||
|
||||
local print=function(t) minetest.log("action", t) minetest.chat_send_all(t) end
|
||||
|
||||
advtrains.train_accel_force=5--per second and divided by number of wagons
|
||||
advtrains.train_brake_force=3--per second, not divided by number of wagons
|
||||
advtrains.train_emerg_force=10--for emergency brakes(when going off track)
|
||||
|
||||
advtrains.audit_interval=10
|
||||
|
||||
advtrains.all_traintypes={}
|
||||
function advtrains.register_train_type(name, drives_on, max_speed)
|
||||
advtrains.all_traintypes[name]={}
|
||||
advtrains.all_traintypes[name].drives_on=drives_on
|
||||
advtrains.all_traintypes[name].max_speed=max_speed or 10
|
||||
end
|
||||
|
||||
|
||||
advtrains.trains={}
|
||||
|
||||
--load initially
|
||||
advtrains.fpath=minetest.get_worldpath().."/advtrains"
|
||||
local file, err = io.open(advtrains.fpath, "r")
|
||||
if not file then
|
||||
local er=err or "Unknown Error"
|
||||
print("[advtrains]Failed loading advtrains save file "..er)
|
||||
else
|
||||
local tbl = minetest.deserialize(file:read("*a"))
|
||||
if type(tbl) == "table" then
|
||||
advtrains.trains=tbl
|
||||
end
|
||||
file:close()
|
||||
end
|
||||
|
||||
|
||||
advtrains.save = function()
|
||||
--print("[advtrains]saving")
|
||||
advtrains.invalidate_all_paths()
|
||||
local datastr = minetest.serialize(advtrains.trains)
|
||||
if not datastr then
|
||||
minetest.log("error", "[advtrains] Failed to serialize train data!")
|
||||
return
|
||||
end
|
||||
local file, err = io.open(advtrains.fpath, "w")
|
||||
if err then
|
||||
return err
|
||||
end
|
||||
file:write(datastr)
|
||||
file:close()
|
||||
end
|
||||
minetest.register_on_shutdown(advtrains.save)
|
||||
|
||||
advtrains.save_and_audit_timer=advtrains.audit_interval
|
||||
minetest.register_globalstep(function(dtime)
|
||||
advtrains.save_and_audit_timer=advtrains.save_and_audit_timer-dtime
|
||||
if advtrains.save_and_audit_timer<=0 then
|
||||
--print("[advtrains] audit step")
|
||||
--clean up orphaned trains
|
||||
for k,v in pairs(advtrains.trains) do
|
||||
advtrains.update_trainpart_properties(k)
|
||||
if #v.trainparts==0 then
|
||||
advtrains.trains[k]=nil
|
||||
end
|
||||
end
|
||||
--save
|
||||
advtrains.save()
|
||||
advtrains.save_and_audit_timer=advtrains.audit_interval
|
||||
end
|
||||
--regular train step
|
||||
for k,v in pairs(advtrains.trains) do
|
||||
advtrains.train_step(k, v, dtime)
|
||||
end
|
||||
end)
|
||||
|
||||
function advtrains.train_step(id, train, dtime)
|
||||
|
||||
--TODO check for all vars to be present
|
||||
|
||||
--if not train.last_pos then advtrains.trains[id]=nil return end
|
||||
|
||||
if not advtrains.pathpredict(id, train) then
|
||||
--print("pathpredict failed(returned false)")
|
||||
train.velocity=0
|
||||
train.tarvelocity=0
|
||||
return
|
||||
end
|
||||
|
||||
local path=advtrains.get_or_create_path(id, train)
|
||||
if not path then
|
||||
train.velocity=0
|
||||
train.tarvelocity=0
|
||||
--print("train has no path")
|
||||
return
|
||||
end
|
||||
--apply off-track handling:
|
||||
local front_off_track=train.max_index_on_track and train.index>train.max_index_on_track
|
||||
local back_off_track=train.min_index_on_track and (train.index-train.trainlen)<train.min_index_on_track
|
||||
if front_off_track and back_off_track then--allow movement in both directions
|
||||
if train.tarvelocity>1 then train.tarvelocity=1 end
|
||||
if train.tarvelocity<-1 then train.tarvelocity=-1 end
|
||||
elseif front_off_track then--allow movement only backward
|
||||
if train.tarvelocity>0 then train.tarvelocity=0 end
|
||||
if train.tarvelocity<-1 then train.tarvelocity=-1 end
|
||||
elseif back_off_track then--allow movement only forward
|
||||
if train.tarvelocity>1 then train.tarvelocity=1 end
|
||||
if train.tarvelocity<0 then train.tarvelocity=0 end
|
||||
end
|
||||
|
||||
--move
|
||||
if not train.velocity then
|
||||
train.velocity=0
|
||||
end
|
||||
train.index=train.index and train.index+((train.velocity/(train.path_dist[math.floor(train.index)] or 1))*dtime) or 0
|
||||
--check for collisions by finding objects
|
||||
--front
|
||||
local search_radius=4
|
||||
|
||||
local posfront=path[math.floor(train.index+1)]
|
||||
if posfront then
|
||||
local objrefs=minetest.get_objects_inside_radius(posfront, search_radius)
|
||||
for _,v in pairs(objrefs) do
|
||||
local le=v:get_luaentity()
|
||||
if le and le.is_wagon and le.initialized and le.train_id~=id then
|
||||
advtrains.try_connect_trains(id, le.train_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
local posback=path[math.floor(train.index-(train.trainlen or 0)-1)]
|
||||
if posback then
|
||||
local objrefs=minetest.get_objects_inside_radius(posback, search_radius)
|
||||
for _,v in pairs(objrefs) do
|
||||
local le=v:get_luaentity()
|
||||
if le and le.is_wagon and le.initialized and le.train_id~=id then
|
||||
advtrains.try_connect_trains(id, le.train_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
--handle collided_with_env
|
||||
if train.recently_collided_with_env then
|
||||
train.tarvelocity=0
|
||||
if train.velocity==0 then
|
||||
train.recently_collided_with_env=false--reset status when stopped
|
||||
end
|
||||
end
|
||||
if train.locomotives_in_train==0 then
|
||||
train.tarvelocity=0
|
||||
end
|
||||
--apply tarvel(but with physics in mind!)
|
||||
if train.velocity~=train.tarvelocity then
|
||||
local applydiff=0
|
||||
local mass=#train.trainparts
|
||||
local diff=math.abs(train.tarvelocity)-math.abs(train.velocity)
|
||||
if diff>0 then--accelerating, force will be brought on only by locomotives.
|
||||
--print("accelerating with default force")
|
||||
applydiff=(math.min((advtrains.train_accel_force*train.locomotives_in_train*dtime)/mass, math.abs(diff)))
|
||||
else--decelerating
|
||||
if front_off_track or back_off_track or train.recently_collided_with_env then --every wagon has a brake, so not divided by mass.
|
||||
--print("braking with emergency force")
|
||||
applydiff=(math.min((advtrains.train_emerg_force*dtime), math.abs(diff)))
|
||||
else
|
||||
--print("braking with default force")
|
||||
applydiff=(math.min((advtrains.train_brake_force*dtime), math.abs(diff)))
|
||||
end
|
||||
end
|
||||
train.velocity=train.velocity+(applydiff*math.sign(train.tarvelocity-train.velocity))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--the 'leader' concept has been overthrown, we won't rely on MT's "buggy object management"
|
||||
--structure of train table:
|
||||
--[[
|
||||
trains={
|
||||
[train_id]={
|
||||
trainparts={
|
||||
[n]=wagon_id
|
||||
}
|
||||
path={path}
|
||||
velocity
|
||||
tarvelocity
|
||||
index
|
||||
trainlen
|
||||
path_inv_level
|
||||
last_pos |
|
||||
last_dir | for pathpredicting.
|
||||
no_connect_for_movements (index way counter for when not to connect again) TODO implement
|
||||
}
|
||||
}
|
||||
--a wagon itself has the following properties:
|
||||
wagon={
|
||||
unique_id
|
||||
train_id
|
||||
pos_in_train (is index difference, including train_span stuff)
|
||||
pos_in_trainparts (is index in trainparts tabel of trains)
|
||||
}
|
||||
inherited by metatable:
|
||||
wagon_proto={
|
||||
wagon_span
|
||||
}
|
||||
]]
|
||||
|
||||
--returns new id
|
||||
function advtrains.create_new_train_at(pos, pos_prev, traintype)
|
||||
local newtrain_id=os.time()..os.clock()
|
||||
while advtrains.trains[newtrain_id] do newtrain_id=os.time()..os.clock() end--ensure uniqueness(will be unneccessary)
|
||||
|
||||
advtrains.trains[newtrain_id]={}
|
||||
advtrains.trains[newtrain_id].last_pos=pos
|
||||
advtrains.trains[newtrain_id].last_pos_prev=pos_prev
|
||||
advtrains.trains[newtrain_id].traintype=traintype
|
||||
advtrains.trains[newtrain_id].tarvelocity=0
|
||||
advtrains.trains[newtrain_id].velocity=0
|
||||
advtrains.trains[newtrain_id].trainparts={}
|
||||
return newtrain_id
|
||||
end
|
||||
|
||||
--returns false on failure. handle this case!
|
||||
function advtrains.pathpredict(id, train)
|
||||
|
||||
--print("pos ",x,y,z)
|
||||
--::rerun::
|
||||
if not train.index then train.index=0 end
|
||||
if not train.path or #train.path<2 then
|
||||
if not train.last_pos then
|
||||
--no chance to recover
|
||||
print("[advtrains]train hasn't saved last-pos, removing train.")
|
||||
advtrains.train[id]=nil
|
||||
return false
|
||||
end
|
||||
|
||||
local node=minetest.get_node_or_nil(advtrains.round_vector_floor_y(train.last_pos))
|
||||
if not node then
|
||||
--print("pathpredict:last_pos node "..minetest.pos_to_string(advtrains.round_vector_floor_y(train.last_pos)).." is nil. block probably not loaded")
|
||||
return nil
|
||||
end
|
||||
local nodename=node.name
|
||||
|
||||
if(not advtrains.is_track_and_drives_on(nodename, advtrains.all_traintypes[train.traintype].drives_on)) then
|
||||
advtrains.dumppath(train.path)
|
||||
print("at index "..train.index)
|
||||
print("[advtrains]no track here, (fail) removing train.")
|
||||
advtrains.trains[id]=nil
|
||||
return false
|
||||
end
|
||||
if not train.last_pos_prev then
|
||||
--no chance to recover
|
||||
print("[advtrains]train hasn't saved last-pos_prev, removing train.")
|
||||
advtrains.trains[id]=nil
|
||||
return false
|
||||
end
|
||||
local prevnode=minetest.get_node_or_nil(advtrains.round_vector_floor_y(train.last_pos_prev))
|
||||
if not prevnode then
|
||||
--print("pathpredict:last_pos_prev node "..minetest.pos_to_string(advtrains.round_vector_floor_y(train.last_pos_prev)).." is nil. block probably not loaded")
|
||||
return nil
|
||||
end
|
||||
local prevnodename=prevnode.name
|
||||
|
||||
if(not advtrains.is_track_and_drives_on(prevnodename, advtrains.all_traintypes[train.traintype].drives_on)) then
|
||||
print("[advtrains]no track at prev, (fail) removing train.")
|
||||
advtrains.trains[id]=nil
|
||||
return false
|
||||
end
|
||||
|
||||
local conn1, conn2=advtrains.get_track_connections(nodename, node.param2)
|
||||
|
||||
train.index=(train.restore_add_index or 0)+(train.savedpos_off_track_index_offset or 0)
|
||||
--restore_add_index is set by save() to prevent trains hopping to next round index. should be between -0.5 and 0.5
|
||||
--savedpos_off_track_index_offset is set if train went off track. see below.
|
||||
train.path={}
|
||||
train.path_dist={}
|
||||
train.path[0]=train.last_pos
|
||||
train.path[-1]=train.last_pos_prev
|
||||
train.path_dist[-1]=vector.distance(train.last_pos, train.last_pos_prev)
|
||||
end
|
||||
|
||||
local maxn=advtrains.maxN(train.path)
|
||||
while (maxn-train.index) < 2 do--pregenerate
|
||||
--print("[advtrains]maxn conway for ",maxn,minetest.pos_to_string(path[maxn]),maxn-1,minetest.pos_to_string(path[maxn-1]))
|
||||
local conway=advtrains.conway(train.path[maxn], train.path[maxn-1], advtrains.all_traintypes[train.traintype].drives_on)
|
||||
if conway then
|
||||
train.path[maxn+1]=conway
|
||||
train.max_index_on_track=maxn
|
||||
else
|
||||
--do as if nothing has happened and preceed with path
|
||||
--but do not update max_index_on_track
|
||||
print("over-generating path max to index "..maxn+1)
|
||||
train.path[maxn+1]=vector.add(train.path[maxn], vector.subtract(train.path[maxn], train.path[maxn-1]))
|
||||
end
|
||||
train.path_dist[maxn]=vector.distance(train.path[maxn+1], train.path[maxn])
|
||||
maxn=advtrains.maxN(train.path)
|
||||
end
|
||||
|
||||
local minn=advtrains.minN(train.path)
|
||||
while (train.index-minn) < (train.trainlen or 0) + 2 do --post_generate. has to be at least trainlen.
|
||||
--print("[advtrains]minn conway for ",minn,minetest.pos_to_string(path[minn]),minn+1,minetest.pos_to_string(path[minn+1]))
|
||||
local conway=advtrains.conway(train.path[minn], train.path[minn+1], advtrains.all_traintypes[train.traintype].drives_on)
|
||||
if conway then
|
||||
train.path[minn-1]=conway
|
||||
train.min_index_on_track=minn
|
||||
else
|
||||
--do as if nothing has happened and preceed with path
|
||||
--but do not update min_index_on_track
|
||||
print("over-generating path min to index "..minn-1)
|
||||
train.path[minn-1]=vector.add(train.path[minn], vector.subtract(train.path[minn], train.path[minn+1]))
|
||||
end
|
||||
train.path_dist[minn-1]=vector.distance(train.path[minn], train.path[minn-1])
|
||||
minn=advtrains.minN(train.path)
|
||||
end
|
||||
if not train.min_index_on_track then train.min_index_on_track=0 end
|
||||
if not train.max_index_on_track then train.max_index_on_track=0 end
|
||||
|
||||
--make pos/yaw available for possible recover calls
|
||||
if train.max_index_on_track<train.index then --whoops, train went too far. the saved position will be the last one that lies on a track, and savedpos_off_track_index_offset will hold how far to go from here
|
||||
train.savedpos_off_track_index_offset=train.index-train.max_index_on_track
|
||||
train.last_pos=train.path[train.max_index_on_track]
|
||||
train.last_pos_prev=train.path[train.max_index_on_track-1]
|
||||
--print("train is off-track (front), last positions kept at "..minetest.pos_to_string(train.last_pos).." / "..minetest.pos_to_string(train.last_pos_prev))
|
||||
elseif train.min_index_on_track+1>train.index then --whoops, train went even more far. same behavior
|
||||
train.savedpos_off_track_index_offset=train.index-train.min_index_on_track
|
||||
train.last_pos=train.path[train.min_index_on_track+1]
|
||||
train.last_pos_prev=train.path[train.min_index_on_track]
|
||||
--print("train is off-track (back), last positions kept at "..minetest.pos_to_string(train.last_pos).." / "..minetest.pos_to_string(train.last_pos_prev))
|
||||
else --regular case
|
||||
train.savedpos_off_track_index_offset=nil
|
||||
train.last_pos=train.path[math.floor(train.index+0.5)]
|
||||
train.last_pos_prev=train.path[math.floor(train.index-0.5)]
|
||||
end
|
||||
return train.path
|
||||
end
|
||||
|
||||
function advtrains.get_or_create_path(id, train)
|
||||
if not train.path then return advtrains.pathpredict(id, train) end
|
||||
|
||||
return train.path
|
||||
end
|
||||
|
||||
function advtrains.add_wagon_to_train(wagon, train_id, index)
|
||||
local train=advtrains.trains[train_id]
|
||||
if index then
|
||||
table.insert(train.trainparts, index, wagon.unique_id)
|
||||
else
|
||||
table.insert(train.trainparts, wagon.unique_id)
|
||||
end
|
||||
--this is not the usual case!!!
|
||||
--we may set initialized because the wagon has no chance to step()
|
||||
wagon.initialized=true
|
||||
advtrains.update_trainpart_properties(train_id)
|
||||
end
|
||||
function advtrains.update_trainpart_properties(train_id, invert_flipstate)
|
||||
local train=advtrains.trains[train_id]
|
||||
local rel_pos=0
|
||||
local count_l=0
|
||||
for i, w_id in ipairs(train.trainparts) do
|
||||
for _,wagon in pairs(minetest.luaentities) do
|
||||
if wagon.is_wagon and wagon.initialized and wagon.unique_id==w_id then
|
||||
rel_pos=rel_pos+wagon.wagon_span
|
||||
wagon.train_id=train_id
|
||||
wagon.pos_in_train=rel_pos
|
||||
wagon.pos_in_trainparts=i
|
||||
wagon.old_velocity_vector=nil
|
||||
if wagon.is_locomotive then
|
||||
count_l=count_l+1
|
||||
end
|
||||
if invert_flipstate then
|
||||
wagon.wagon_flipped = not wagon.wagon_flipped
|
||||
end
|
||||
rel_pos=rel_pos+wagon.wagon_span
|
||||
end
|
||||
end
|
||||
end
|
||||
train.trainlen=rel_pos
|
||||
train.locomotives_in_train=count_l
|
||||
end
|
||||
|
||||
function advtrains.split_train_at_wagon(wagon)
|
||||
--get train
|
||||
local train=advtrains.trains[wagon.train_id]
|
||||
local pos_for_new_train=advtrains.get_or_create_path(wagon.train_id, train)[math.floor((train.index or 0)-wagon.pos_in_train-0.5)]
|
||||
local pos_for_new_train_prev=advtrains.get_or_create_path(wagon.train_id, train)[math.floor((train.index or 0)-wagon.pos_in_train-1.5)]
|
||||
|
||||
--before doing anything, check if both are rails. else do not allow
|
||||
if not pos_for_new_train then
|
||||
print("split_train: pos_for_new_train not set")
|
||||
return false
|
||||
end
|
||||
local node=minetest.get_node_or_nil(advtrains.round_vector_floor_y(pos_for_new_train))
|
||||
if not node then
|
||||
print("split_train: pos_for_new_train node "..minetest.pos_to_string(advtrains.round_vector_floor_y(pos_for_new_train)).." is nil. block probably not loaded")
|
||||
return nil
|
||||
end
|
||||
local nodename=node.name
|
||||
|
||||
if(not advtrains.is_track_and_drives_on(nodename, advtrains.all_traintypes[train.traintype].drives_on)) then
|
||||
print("split_train: pos_for_new_train "..minetest.pos_to_string(advtrains.round_vector_floor_y(pos_for_new_train_prev)).." is not a rail")
|
||||
return false
|
||||
end
|
||||
if not train.last_pos_prev then
|
||||
print("split_train: pos_for_new_train_prev not set")
|
||||
return false
|
||||
end
|
||||
local prevnode=minetest.get_node_or_nil(advtrains.round_vector_floor_y(pos_for_new_train_prev))
|
||||
if not node then
|
||||
print("split_train: pos_for_new_train_prev node "..minetest.pos_to_string(advtrains.round_vector_floor_y(pos_for_new_train_prev)).." is nil. block probably not loaded")
|
||||
return false
|
||||
end
|
||||
local prevnodename=prevnode.name
|
||||
|
||||
if(not advtrains.is_track_and_drives_on(prevnodename, advtrains.all_traintypes[train.traintype].drives_on)) then
|
||||
print("split_train: pos_for_new_train_prev "..minetest.pos_to_string(advtrains.round_vector_floor_y(pos_for_new_train_prev)).." is not a rail")
|
||||
return false
|
||||
end
|
||||
|
||||
--create subtrain
|
||||
local newtrain_id=advtrains.create_new_train_at(pos_for_new_train, pos_for_new_train_prev, train.traintype)
|
||||
local newtrain=advtrains.trains[newtrain_id]
|
||||
--insert all wagons to new train
|
||||
for k,v in ipairs(train.trainparts) do
|
||||
if k>=wagon.pos_in_trainparts then
|
||||
table.insert(newtrain.trainparts, v)
|
||||
train.trainparts[k]=nil
|
||||
end
|
||||
end
|
||||
--update train parts
|
||||
advtrains.update_trainpart_properties(wagon.train_id)--atm it still is the desierd id.
|
||||
advtrains.update_trainpart_properties(newtrain_id)
|
||||
train.tarvelocity=0
|
||||
newtrain.velocity=train.velocity
|
||||
newtrain.tarvelocity=0
|
||||
end
|
||||
|
||||
--there are 4 cases:
|
||||
--1/2. F<->R F<->R regular, put second train behind first
|
||||
--->frontpos of first train will match backpos of second
|
||||
--3. F<->R R<->F flip one of these trains, take the other as new train
|
||||
--->backpos's will match
|
||||
--4. R<->F F<->R flip one of these trains and take it as new parent
|
||||
--->frontpos's will match
|
||||
function advtrains.try_connect_trains(id1, id2)
|
||||
local train1=advtrains.trains[id1]
|
||||
local train2=advtrains.trains[id2]
|
||||
if not train1 or not train2 then return end
|
||||
if not train1.path or not train2.path then return end
|
||||
if train1.traintype~=train2.traintype then
|
||||
--TODO implement collision without connection
|
||||
return
|
||||
end
|
||||
if #train1.trainparts==0 or #train2.trainparts==0 then return end
|
||||
|
||||
local frontpos1=train1.path[math.floor(train1.index+0.5)]
|
||||
local backpos1=train1.path[math.floor(train1.index-(train1.trainlen or 2)+0.5)]
|
||||
local frontpos2=train2.path[math.floor(train2.index+0.5)]
|
||||
local backpos2=train2.path[math.floor(train2.index-(train1.trainlen or 2)+0.5)]
|
||||
|
||||
if not frontpos1 or not frontpos2 or not backpos1 or not backpos2 then return end
|
||||
|
||||
--case 1 (first train is front)
|
||||
if vector.equals(frontpos2, backpos1) then
|
||||
advtrains.do_connect_trains(id1, id2)
|
||||
--case 2 (second train is front)
|
||||
elseif vector.equals(frontpos1, backpos2) then
|
||||
advtrains.do_connect_trains(id2, id1)
|
||||
--case 3
|
||||
elseif vector.equals(backpos2, backpos1) then
|
||||
advtrains.invert_train(id2)
|
||||
advtrains.do_connect_trains(id1, id2)
|
||||
--case 4
|
||||
elseif vector.equals(frontpos2, frontpos1) then
|
||||
advtrains.invert_train(id1)
|
||||
advtrains.do_connect_trains(id1, id2)
|
||||
end
|
||||
end
|
||||
function advtrains.do_connect_trains(first_id, second_id)
|
||||
local first_wagoncnt=#advtrains.trains[first_id].trainparts
|
||||
local second_wagoncnt=#advtrains.trains[second_id].trainparts
|
||||
|
||||
for _,v in ipairs(advtrains.trains[second_id].trainparts) do
|
||||
table.insert(advtrains.trains[first_id].trainparts, v)
|
||||
end
|
||||
--kick it like physics (with mass being #wagons)
|
||||
local new_velocity=((advtrains.trains[first_id].velocity*first_wagoncnt)+(advtrains.trains[second_id].velocity*second_wagoncnt))/(first_wagoncnt+second_wagoncnt)
|
||||
advtrains.trains[second_id]=nil
|
||||
advtrains.update_trainpart_properties(first_id)
|
||||
advtrains.trains[first_id].velocity=new_velocity
|
||||
advtrains.trains[first_id].tarvelocity=0
|
||||
end
|
||||
|
||||
function advtrains.invert_train(train_id)
|
||||
local train=advtrains.trains[train_id]
|
||||
|
||||
local old_path=advtrains.get_or_create_path(train_id, train)
|
||||
train.path={}
|
||||
train.index=-train.index+train.trainlen
|
||||
train.velocity=-train.velocity
|
||||
train.tarvelocity=-train.tarvelocity
|
||||
for k,v in pairs(old_path) do
|
||||
train.path[-k]=v
|
||||
end
|
||||
local old_trainparts=train.trainparts
|
||||
train.trainparts={}
|
||||
for k,v in ipairs(old_trainparts) do
|
||||
table.insert(train.trainparts, 1, v)--notice insertion at first place
|
||||
end
|
||||
advtrains.update_trainpart_properties(train_id, true)
|
||||
end
|
||||
|
||||
function advtrains.is_train_at_pos(pos)
|
||||
--print("istrainat: pos "..minetest.pos_to_string(pos))
|
||||
local checked_trains={}
|
||||
local objrefs=minetest.get_objects_inside_radius(pos, 2)
|
||||
for _,v in pairs(objrefs) do
|
||||
local le=v:get_luaentity()
|
||||
if le and le.is_wagon and le.initialized and le.train_id and not checked_trains[le.train_id] then
|
||||
--print("istrainat: checking "..le.train_id)
|
||||
checked_trains[le.train_id]=true
|
||||
local path=advtrains.get_or_create_path(le.train_id, le:train())
|
||||
if path then
|
||||
--print("has path")
|
||||
for i=math.floor(le:train().index-le:train().trainlen+0.5),math.floor(le:train().index+0.5) do
|
||||
if path[i] then
|
||||
--print("has pathitem "..i.." "..minetest.pos_to_string(path[i]))
|
||||
if vector.equals(advtrains.round_vector_floor_y(path[i]), pos) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
function advtrains.invalidate_all_paths()
|
||||
--print("invalidating all paths")
|
||||
for k,v in pairs(advtrains.trains) do
|
||||
if v.index then
|
||||
v.restore_add_index=v.index-math.floor(v.index+0.5)
|
||||
end
|
||||
v.path=nil
|
||||
v.index=nil
|
||||
v.min_index_on_track=nil
|
||||
v.max_index_on_track=nil
|
||||
end
|
||||
end
|
|
@ -0,0 +1,542 @@
|
|||
--trainlogic.lua
|
||||
--controls train entities stuff about connecting/disconnecting/colliding trains and other things
|
||||
|
||||
local print=function(t) minetest.log("action", t) minetest.chat_send_all(t) end
|
||||
|
||||
advtrains.train_accel_force=5--per second and divided by number of wagons
|
||||
advtrains.train_brake_force=3--per second, not divided by number of wagons
|
||||
advtrains.train_emerg_force=10--for emergency brakes(when going off track)
|
||||
|
||||
advtrains.audit_interval=10
|
||||
|
||||
advtrains.all_traintypes={}
|
||||
function advtrains.register_train_type(name, drives_on, max_speed)
|
||||
advtrains.all_traintypes[name]={}
|
||||
advtrains.all_traintypes[name].drives_on=drives_on
|
||||
advtrains.all_traintypes[name].max_speed=max_speed or 10
|
||||
end
|
||||
|
||||
|
||||
advtrains.trains={}
|
||||
|
||||
--load initially
|
||||
advtrains.fpath=minetest.get_worldpath().."/advtrains"
|
||||
local file, err = io.open(advtrains.fpath, "r")
|
||||
if not file then
|
||||
local er=err or "Unknown Error"
|
||||
print("[advtrains]Failed loading advtrains save file "..er)
|
||||
else
|
||||
local tbl = minetest.deserialize(file:read("*a"))
|
||||
if type(tbl) == "table" then
|
||||
advtrains.trains=tbl
|
||||
end
|
||||
file:close()
|
||||
end
|
||||
|
||||
|
||||
advtrains.save = function()
|
||||
--print("[advtrains]saving")
|
||||
advtrains.invalidate_all_paths()
|
||||
local datastr = minetest.serialize(advtrains.trains)
|
||||
if not datastr then
|
||||
minetest.log("error", "[advtrains] Failed to serialize train data!")
|
||||
return
|
||||
end
|
||||
local file, err = io.open(advtrains.fpath, "w")
|
||||
if err then
|
||||
return err
|
||||
end
|
||||
file:write(datastr)
|
||||
file:close()
|
||||
end
|
||||
minetest.register_on_shutdown(advtrains.save)
|
||||
|
||||
advtrains.save_and_audit_timer=advtrains.audit_interval
|
||||
minetest.register_globalstep(function(dtime)
|
||||
advtrains.save_and_audit_timer=advtrains.save_and_audit_timer-dtime
|
||||
if advtrains.save_and_audit_timer<=0 then
|
||||
--print("[advtrains] audit step")
|
||||
--clean up orphaned trains
|
||||
for k,v in pairs(advtrains.trains) do
|
||||
advtrains.update_trainpart_properties(k)
|
||||
if #v.trainparts==0 then
|
||||
advtrains.trains[k]=nil
|
||||
end
|
||||
end
|
||||
--save
|
||||
advtrains.save()
|
||||
advtrains.save_and_audit_timer=advtrains.audit_interval
|
||||
end
|
||||
--regular train step
|
||||
for k,v in pairs(advtrains.trains) do
|
||||
advtrains.train_step(k, v, dtime)
|
||||
end
|
||||
end)
|
||||
|
||||
function advtrains.train_step(id, train, dtime)
|
||||
|
||||
--TODO check for all vars to be present
|
||||
|
||||
--if not train.last_pos then advtrains.trains[id]=nil return end
|
||||
|
||||
if not advtrains.pathpredict(id, train) then
|
||||
--print("pathpredict failed(returned false)")
|
||||
train.velocity=0
|
||||
train.tarvelocity=0
|
||||
return
|
||||
end
|
||||
|
||||
local path=advtrains.get_or_create_path(id, train)
|
||||
if not path then
|
||||
train.velocity=0
|
||||
train.tarvelocity=0
|
||||
--print("train has no path")
|
||||
return
|
||||
end
|
||||
--apply off-track handling:
|
||||
local front_off_track=train.max_index_on_track and train.index>train.max_index_on_track
|
||||
local back_off_track=train.min_index_on_track and (train.index-train.trainlen)<train.min_index_on_track
|
||||
if front_off_track and back_off_track then--allow movement in both directions
|
||||
if train.tarvelocity>1 then train.tarvelocity=1 end
|
||||
if train.tarvelocity<-1 then train.tarvelocity=-1 end
|
||||
elseif front_off_track then--allow movement only backward
|
||||
if train.tarvelocity>0 then train.tarvelocity=0 end
|
||||
if train.tarvelocity<-1 then train.tarvelocity=-1 end
|
||||
elseif back_off_track then--allow movement only forward
|
||||
if train.tarvelocity>1 then train.tarvelocity=1 end
|
||||
if train.tarvelocity<0 then train.tarvelocity=0 end
|
||||
end
|
||||
|
||||
--move
|
||||
if not train.velocity then
|
||||
train.velocity=0
|
||||
end
|
||||
train.index=train.index and train.index+((train.velocity/(train.path_dist[math.floor(train.index)] or 1))*dtime) or 0
|
||||
--check for collisions by finding objects
|
||||
--front
|
||||
local search_radius=4
|
||||
|
||||
local posfront=path[math.floor(train.index+1)]
|
||||
if posfront then
|
||||
local objrefs=minetest.get_objects_inside_radius(posfront, search_radius)
|
||||
for _,v in pairs(objrefs) do
|
||||
local le=v:get_luaentity()
|
||||
if le and le.is_wagon and le.initialized and le.train_id~=id then
|
||||
advtrains.try_connect_trains(id, le.train_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
local posback=path[math.floor(train.index-(train.trainlen or 0)-1)]
|
||||
if posback then
|
||||
local objrefs=minetest.get_objects_inside_radius(posback, search_radius)
|
||||
for _,v in pairs(objrefs) do
|
||||
local le=v:get_luaentity()
|
||||
if le and le.is_wagon and le.initialized and le.train_id~=id then
|
||||
advtrains.try_connect_trains(id, le.train_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
--handle collided_with_env
|
||||
if train.recently_collided_with_env then
|
||||
train.tarvelocity=0
|
||||
if train.velocity==0 then
|
||||
train.recently_collided_with_env=false--reset status when stopped
|
||||
end
|
||||
end
|
||||
if train.locomotives_in_train==0 then
|
||||
train.tarvelocity=0
|
||||
end
|
||||
--apply tarvel(but with physics in mind!)
|
||||
if train.velocity~=train.tarvelocity then
|
||||
local applydiff=0
|
||||
local mass=#train.trainparts
|
||||
local diff=math.abs(train.tarvelocity)-math.abs(train.velocity)
|
||||
if diff>0 then--accelerating, force will be brought on only by locomotives.
|
||||
--print("accelerating with default force")
|
||||
applydiff=(math.min((advtrains.train_accel_force*train.locomotives_in_train*dtime)/mass, math.abs(diff)))
|
||||
else--decelerating
|
||||
if front_off_track or back_off_track or train.recently_collided_with_env then --every wagon has a brake, so not divided by mass.
|
||||
--print("braking with emergency force")
|
||||
applydiff=(math.min((advtrains.train_emerg_force*dtime), math.abs(diff)))
|
||||
else
|
||||
--print("braking with default force")
|
||||
applydiff=(math.min((advtrains.train_brake_force*dtime), math.abs(diff)))
|
||||
end
|
||||
end
|
||||
train.velocity=train.velocity+(applydiff*math.sign(train.tarvelocity-train.velocity))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--the 'leader' concept has been overthrown, we won't rely on MT's "buggy object management"
|
||||
--structure of train table:
|
||||
--[[
|
||||
trains={
|
||||
[train_id]={
|
||||
trainparts={
|
||||
[n]=wagon_id
|
||||
}
|
||||
path={path}
|
||||
velocity
|
||||
tarvelocity
|
||||
index
|
||||
trainlen
|
||||
path_inv_level
|
||||
last_pos |
|
||||
last_dir | for pathpredicting.
|
||||
no_connect_for_movements (index way counter for when not to connect again) TODO implement
|
||||
}
|
||||
}
|
||||
--a wagon itself has the following properties:
|
||||
wagon={
|
||||
unique_id
|
||||
train_id
|
||||
pos_in_train (is index difference, including train_span stuff)
|
||||
pos_in_trainparts (is index in trainparts tabel of trains)
|
||||
}
|
||||
inherited by metatable:
|
||||
wagon_proto={
|
||||
wagon_span
|
||||
}
|
||||
]]
|
||||
|
||||
--returns new id
|
||||
function advtrains.create_new_train_at(pos, pos_prev, traintype)
|
||||
local newtrain_id=os.time()..os.clock()
|
||||
while advtrains.trains[newtrain_id] do newtrain_id=os.time()..os.clock() end--ensure uniqueness(will be unneccessary)
|
||||
|
||||
advtrains.trains[newtrain_id]={}
|
||||
advtrains.trains[newtrain_id].last_pos=pos
|
||||
advtrains.trains[newtrain_id].last_pos_prev=pos_prev
|
||||
advtrains.trains[newtrain_id].traintype=traintype
|
||||
advtrains.trains[newtrain_id].tarvelocity=0
|
||||
advtrains.trains[newtrain_id].velocity=0
|
||||
advtrains.trains[newtrain_id].trainparts={}
|
||||
return newtrain_id
|
||||
end
|
||||
|
||||
--returns false on failure. handle this case!
|
||||
function advtrains.pathpredict(id, train)
|
||||
|
||||
--print("pos ",x,y,z)
|
||||
--::rerun::
|
||||
if not train.index then train.index=0 end
|
||||
if not train.path or #train.path<2 then
|
||||
if not train.last_pos then
|
||||
--no chance to recover
|
||||
print("[advtrains]train hasn't saved last-pos, removing train.")
|
||||
advtrains.train[id]=nil
|
||||
return false
|
||||
end
|
||||
local node=minetest.get_node_or_nil(advtrains.round_vector_floor_y(train.last_pos))
|
||||
if not node then
|
||||
--print("pathpredict:last_pos node "..minetest.pos_to_string(advtrains.round_vector_floor_y(train.last_pos)).." is nil. block probably not loaded")
|
||||
return nil
|
||||
end
|
||||
local nodename=node.name
|
||||
|
||||
if(not advtrains.is_track_and_drives_on(nodename, advtrains.all_traintypes[train.traintype].drives_on)) then
|
||||
advtrains.dumppath(train.path)
|
||||
print("at index "..train.index)
|
||||
print("[advtrains]no track here, (fail) removing train.")
|
||||
advtrains.trains[id]=nil
|
||||
return false
|
||||
end
|
||||
if not train.last_pos_prev then
|
||||
--no chance to recover
|
||||
print("[advtrains]train hasn't saved last-pos_prev, removing train.")
|
||||
advtrains.trains[id]=nil
|
||||
return false
|
||||
end
|
||||
local prevnode=minetest.get_node_or_nil(advtrains.round_vector_floor_y(train.last_pos_prev))
|
||||
if not prevnode then
|
||||
--print("pathpredict:last_pos_prev node "..minetest.pos_to_string(advtrains.round_vector_floor_y(train.last_pos_prev)).." is nil. block probably not loaded")
|
||||
return nil
|
||||
end
|
||||
local prevnodename=prevnode.name
|
||||
|
||||
if(not advtrains.is_track_and_drives_on(prevnodename, advtrains.all_traintypes[train.traintype].drives_on)) then
|
||||
print("[advtrains]no track at prev, (fail) removing train.")
|
||||
advtrains.trains[id]=nil
|
||||
return false
|
||||
end
|
||||
|
||||
local conn1, conn2=advtrains.get_track_connections(nodename, node.param2)
|
||||
|
||||
train.index=(train.restore_add_index or 0)+(train.savedpos_off_track_index_offset or 0)
|
||||
--restore_add_index is set by save() to prevent trains hopping to next round index. should be between -0.5 and 0.5
|
||||
--savedpos_off_track_index_offset is set if train went off track. see below.
|
||||
train.path={}
|
||||
train.path_dist={}
|
||||
train.path[0]=train.last_pos
|
||||
train.path[-1]=train.last_pos_prev
|
||||
train.path_dist[-1]=vector.distance(train.last_pos, train.last_pos_prev)
|
||||
end
|
||||
|
||||
local maxn=advtrains.maxN(train.path)
|
||||
while (maxn-train.index) < 2 do--pregenerate
|
||||
--print("[advtrains]maxn conway for ",maxn,minetest.pos_to_string(path[maxn]),maxn-1,minetest.pos_to_string(path[maxn-1]))
|
||||
local conway=advtrains.conway(train.path[maxn], train.path[maxn-1], advtrains.all_traintypes[train.traintype].drives_on)
|
||||
if conway then
|
||||
train.path[maxn+1]=conway
|
||||
train.max_index_on_track=maxn
|
||||
else
|
||||
--do as if nothing has happened and preceed with path
|
||||
--but do not update max_index_on_track
|
||||
print("over-generating path max to index "..maxn+1)
|
||||
train.path[maxn+1]=vector.add(train.path[maxn], vector.subtract(train.path[maxn], train.path[maxn-1]))
|
||||
end
|
||||
train.path_dist[maxn]=vector.distance(train.path[maxn+1], train.path[maxn])
|
||||
maxn=advtrains.maxN(train.path)
|
||||
end
|
||||
|
||||
local minn=advtrains.minN(train.path)
|
||||
while (train.index-minn) < (train.trainlen or 0) + 2 do --post_generate. has to be at least trainlen.
|
||||
--print("[advtrains]minn conway for ",minn,minetest.pos_to_string(path[minn]),minn+1,minetest.pos_to_string(path[minn+1]))
|
||||
local conway=advtrains.conway(train.path[minn], train.path[minn+1], advtrains.all_traintypes[train.traintype].drives_on)
|
||||
if conway then
|
||||
train.path[minn-1]=conway
|
||||
train.min_index_on_track=minn
|
||||
else
|
||||
--do as if nothing has happened and preceed with path
|
||||
--but do not update min_index_on_track
|
||||
print("over-generating path min to index "..minn-1)
|
||||
train.path[minn-1]=vector.add(train.path[minn], vector.subtract(train.path[minn], train.path[minn+1]))
|
||||
end
|
||||
train.path_dist[minn-1]=vector.distance(train.path[minn], train.path[minn-1])
|
||||
minn=advtrains.minN(train.path)
|
||||
end
|
||||
if not train.min_index_on_track then train.min_index_on_track=0 end
|
||||
if not train.max_index_on_track then train.max_index_on_track=0 end
|
||||
|
||||
--make pos/yaw available for possible recover calls
|
||||
if train.max_index_on_track<train.index then --whoops, train went too far. the saved position will be the last one that lies on a track, and savedpos_off_track_index_offset will hold how far to go from here
|
||||
train.savedpos_off_track_index_offset=train.index-train.max_index_on_track
|
||||
train.last_pos=train.path[train.max_index_on_track]
|
||||
train.last_pos_prev=train.path[train.max_index_on_track-1]
|
||||
--print("train is off-track (front), last positions kept at "..minetest.pos_to_string(train.last_pos).." / "..minetest.pos_to_string(train.last_pos_prev))
|
||||
elseif train.min_index_on_track+1>train.index then --whoops, train went even more far. same behavior
|
||||
train.savedpos_off_track_index_offset=train.index-train.min_index_on_track
|
||||
train.last_pos=train.path[train.min_index_on_track+1]
|
||||
train.last_pos_prev=train.path[train.min_index_on_track]
|
||||
--print("train is off-track (back), last positions kept at "..minetest.pos_to_string(train.last_pos).." / "..minetest.pos_to_string(train.last_pos_prev))
|
||||
else --regular case
|
||||
train.savedpos_off_track_index_offset=nil
|
||||
train.last_pos=train.path[math.floor(train.index+0.5)]
|
||||
train.last_pos_prev=train.path[math.floor(train.index-0.5)]
|
||||
end
|
||||
return train.path
|
||||
end
|
||||
|
||||
function advtrains.get_or_create_path(id, train)
|
||||
if not train.path then return advtrains.pathpredict(id, train) end
|
||||
|
||||
return train.path
|
||||
end
|
||||
|
||||
function advtrains.add_wagon_to_train(wagon, train_id, index)
|
||||
local train=advtrains.trains[train_id]
|
||||
if index then
|
||||
table.insert(train.trainparts, index, wagon.unique_id)
|
||||
else
|
||||
table.insert(train.trainparts, wagon.unique_id)
|
||||
end
|
||||
--this is not the usual case!!!
|
||||
--we may set initialized because the wagon has no chance to step()
|
||||
wagon.initialized=true
|
||||
advtrains.update_trainpart_properties(train_id)
|
||||
end
|
||||
function advtrains.update_trainpart_properties(train_id, invert_flipstate)
|
||||
local train=advtrains.trains[train_id]
|
||||
local rel_pos=0
|
||||
local count_l=0
|
||||
for i, w_id in ipairs(train.trainparts) do
|
||||
for _,wagon in pairs(minetest.luaentities) do
|
||||
if wagon.is_wagon and wagon.initialized and wagon.unique_id==w_id then
|
||||
rel_pos=rel_pos+wagon.wagon_span
|
||||
wagon.train_id=train_id
|
||||
wagon.pos_in_train=rel_pos
|
||||
wagon.pos_in_trainparts=i
|
||||
wagon.old_velocity_vector=nil
|
||||
if wagon.is_locomotive then
|
||||
count_l=count_l+1
|
||||
end
|
||||
if invert_flipstate then
|
||||
wagon.wagon_flipped = not wagon.wagon_flipped
|
||||
end
|
||||
rel_pos=rel_pos+wagon.wagon_span
|
||||
end
|
||||
end
|
||||
end
|
||||
train.trainlen=rel_pos
|
||||
train.locomotives_in_train=count_l
|
||||
end
|
||||
|
||||
function advtrains.split_train_at_wagon(wagon)
|
||||
--get train
|
||||
local train=advtrains.trains[wagon.train_id]
|
||||
local pos_for_new_train=advtrains.get_or_create_path(wagon.train_id, train)[math.floor((train.index or 0)-wagon.pos_in_train-0.5)]
|
||||
local pos_for_new_train_prev=advtrains.get_or_create_path(wagon.train_id, train)[math.floor((train.index or 0)-wagon.pos_in_train-1.5)]
|
||||
|
||||
--before doing anything, check if both are rails. else do not allow
|
||||
if not pos_for_new_train then
|
||||
print("split_train: pos_for_new_train not set")
|
||||
return false
|
||||
end
|
||||
local node=minetest.get_node_or_nil(advtrains.round_vector_floor_y(pos_for_new_train))
|
||||
if not node then
|
||||
print("split_train: pos_for_new_train node "..minetest.pos_to_string(advtrains.round_vector_floor_y(pos_for_new_train)).." is nil. block probably not loaded")
|
||||
return nil
|
||||
end
|
||||
local nodename=node.name
|
||||
|
||||
if(not advtrains.is_track_and_drives_on(nodename, advtrains.all_traintypes[train.traintype].drives_on)) then
|
||||
print("split_train: pos_for_new_train "..minetest.pos_to_string(advtrains.round_vector_floor_y(pos_for_new_train_prev)).." is not a rail")
|
||||
return false
|
||||
end
|
||||
if not train.last_pos_prev then
|
||||
print("split_train: pos_for_new_train_prev not set")
|
||||
return false
|
||||
end
|
||||
local prevnode=minetest.get_node_or_nil(advtrains.round_vector_floor_y(pos_for_new_train_prev))
|
||||
if not node then
|
||||
print("split_train: pos_for_new_train_prev node "..minetest.pos_to_string(advtrains.round_vector_floor_y(pos_for_new_train_prev)).." is nil. block probably not loaded")
|
||||
return false
|
||||
end
|
||||
local prevnodename=prevnode.name
|
||||
|
||||
if(not advtrains.is_track_and_drives_on(prevnodename, advtrains.all_traintypes[train.traintype].drives_on)) then
|
||||
print("split_train: pos_for_new_train_prev "..minetest.pos_to_string(advtrains.round_vector_floor_y(pos_for_new_train_prev)).." is not a rail")
|
||||
return false
|
||||
end
|
||||
|
||||
--create subtrain
|
||||
local newtrain_id=advtrains.create_new_train_at(pos_for_new_train, pos_for_new_train_prev, train.traintype)
|
||||
local newtrain=advtrains.trains[newtrain_id]
|
||||
--insert all wagons to new train
|
||||
for k,v in ipairs(train.trainparts) do
|
||||
if k>=wagon.pos_in_trainparts then
|
||||
table.insert(newtrain.trainparts, v)
|
||||
train.trainparts[k]=nil
|
||||
end
|
||||
end
|
||||
--update train parts
|
||||
advtrains.update_trainpart_properties(wagon.train_id)--atm it still is the desierd id.
|
||||
advtrains.update_trainpart_properties(newtrain_id)
|
||||
train.tarvelocity=0
|
||||
newtrain.velocity=train.velocity
|
||||
newtrain.tarvelocity=0
|
||||
end
|
||||
|
||||
--there are 4 cases:
|
||||
--1/2. F<->R F<->R regular, put second train behind first
|
||||
--->frontpos of first train will match backpos of second
|
||||
--3. F<->R R<->F flip one of these trains, take the other as new train
|
||||
--->backpos's will match
|
||||
--4. R<->F F<->R flip one of these trains and take it as new parent
|
||||
--->frontpos's will match
|
||||
function advtrains.try_connect_trains(id1, id2)
|
||||
local train1=advtrains.trains[id1]
|
||||
local train2=advtrains.trains[id2]
|
||||
if not train1 or not train2 then return end
|
||||
if not train1.path or not train2.path then return end
|
||||
if train1.traintype~=train2.traintype then
|
||||
--TODO implement collision without connection
|
||||
return
|
||||
end
|
||||
if #train1.trainparts==0 or #train2.trainparts==0 then return end
|
||||
|
||||
local frontpos1=train1.path[math.floor(train1.index+0.5)]
|
||||
local backpos1=train1.path[math.floor(train1.index-(train1.trainlen or 2)+0.5)]
|
||||
local frontpos2=train2.path[math.floor(train2.index+0.5)]
|
||||
local backpos2=train2.path[math.floor(train2.index-(train1.trainlen or 2)+0.5)]
|
||||
|
||||
if not frontpos1 or not frontpos2 or not backpos1 or not backpos2 then return end
|
||||
|
||||
--case 1 (first train is front)
|
||||
if vector.equals(frontpos2, backpos1) then
|
||||
advtrains.do_connect_trains(id1, id2)
|
||||
--case 2 (second train is front)
|
||||
elseif vector.equals(frontpos1, backpos2) then
|
||||
advtrains.do_connect_trains(id2, id1)
|
||||
--case 3
|
||||
elseif vector.equals(backpos2, backpos1) then
|
||||
advtrains.invert_train(id2)
|
||||
advtrains.do_connect_trains(id1, id2)
|
||||
--case 4
|
||||
elseif vector.equals(frontpos2, frontpos1) then
|
||||
advtrains.invert_train(id1)
|
||||
advtrains.do_connect_trains(id1, id2)
|
||||
end
|
||||
end
|
||||
function advtrains.do_connect_trains(first_id, second_id)
|
||||
local first_wagoncnt=#advtrains.trains[first_id].trainparts
|
||||
local second_wagoncnt=#advtrains.trains[second_id].trainparts
|
||||
|
||||
for _,v in ipairs(advtrains.trains[second_id].trainparts) do
|
||||
table.insert(advtrains.trains[first_id].trainparts, v)
|
||||
end
|
||||
--kick it like physics (with mass being #wagons)
|
||||
local new_velocity=((advtrains.trains[first_id].velocity*first_wagoncnt)+(advtrains.trains[second_id].velocity*second_wagoncnt))/(first_wagoncnt+second_wagoncnt)
|
||||
advtrains.trains[second_id]=nil
|
||||
advtrains.update_trainpart_properties(first_id)
|
||||
advtrains.trains[first_id].velocity=new_velocity
|
||||
advtrains.trains[first_id].tarvelocity=0
|
||||
end
|
||||
|
||||
function advtrains.invert_train(train_id)
|
||||
local train=advtrains.trains[train_id]
|
||||
|
||||
local old_path=advtrains.get_or_create_path(train_id, train)
|
||||
train.path={}
|
||||
train.index=-train.index+train.trainlen
|
||||
train.velocity=-train.velocity
|
||||
train.tarvelocity=-train.tarvelocity
|
||||
for k,v in pairs(old_path) do
|
||||
train.path[-k]=v
|
||||
end
|
||||
local old_trainparts=train.trainparts
|
||||
train.trainparts={}
|
||||
for k,v in ipairs(old_trainparts) do
|
||||
table.insert(train.trainparts, 1, v)--notice insertion at first place
|
||||
end
|
||||
advtrains.update_trainpart_properties(train_id, true)
|
||||
end
|
||||
|
||||
function advtrains.is_train_at_pos(pos)
|
||||
--print("istrainat: pos "..minetest.pos_to_string(pos))
|
||||
local checked_trains={}
|
||||
local objrefs=minetest.get_objects_inside_radius(pos, 2)
|
||||
for _,v in pairs(objrefs) do
|
||||
local le=v:get_luaentity()
|
||||
if le and le.is_wagon and le.initialized and le.train_id and not checked_trains[le.train_id] then
|
||||
--print("istrainat: checking "..le.train_id)
|
||||
checked_trains[le.train_id]=true
|
||||
local path=advtrains.get_or_create_path(le.train_id, le:train())
|
||||
if path then
|
||||
--print("has path")
|
||||
for i=math.floor(le:train().index-le:train().trainlen+0.5),math.floor(le:train().index+0.5) do
|
||||
if path[i] then
|
||||
--print("has pathitem "..i.." "..minetest.pos_to_string(path[i]))
|
||||
if vector.equals(advtrains.round_vector_floor_y(path[i]), pos) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
function advtrains.invalidate_all_paths()
|
||||
--print("invalidating all paths")
|
||||
for k,v in pairs(advtrains.trains) do
|
||||
if v.index then
|
||||
v.restore_add_index=v.index-math.floor(v.index+0.5)
|
||||
end
|
||||
v.path=nil
|
||||
v.index=nil
|
||||
v.min_index_on_track=nil
|
||||
v.max_index_on_track=nil
|
||||
end
|
||||
end
|
|
@ -0,0 +1,338 @@
|
|||
--atan2 counts angles clockwise, minetest does counterclockwise
|
||||
local print=function(t) minetest.log("action", t) minetest.chat_send_all(t) end
|
||||
|
||||
local wagon={
|
||||
collisionbox = {-0.5,-0.5,-0.5, 0.5,0.5,0.5},
|
||||
--physical = true,
|
||||
visual = "mesh",
|
||||
mesh = "wagon.b3d",
|
||||
visual_size = {x=3, y=3},
|
||||
textures = {"black.png"},
|
||||
is_wagon=true,
|
||||
wagon_span=1,--how many index units of space does this wagon consume
|
||||
attach_offset={x=0, y=0, z=0},
|
||||
view_offset={x=0, y=0, z=0},
|
||||
}
|
||||
|
||||
|
||||
|
||||
function wagon:on_rightclick(clicker)
|
||||
print("[advtrains] wagon rightclick")
|
||||
if not clicker or not clicker:is_player() then
|
||||
return
|
||||
end
|
||||
if not self.initialized then
|
||||
print("[advtrains] not initiaalized")
|
||||
return
|
||||
end
|
||||
if clicker:get_player_control().sneak then
|
||||
advtrains.split_train_at_wagon(self)
|
||||
return
|
||||
end
|
||||
if clicker:get_player_control().aux1 then
|
||||
--advtrains.dumppath(self:train().path)
|
||||
--minetest.chat_send_all("at index "..(self:train().index or "nil"))
|
||||
--advtrains.invert_train(self.train_id)
|
||||
minetest.chat_send_all(dump(self:train()))
|
||||
return
|
||||
end
|
||||
if self.driver and clicker == self.driver then
|
||||
advtrains.set_trainhud(self.driver:get_player_name(), "")
|
||||
self.driver = nil
|
||||
clicker:set_detach()
|
||||
clicker:set_eye_offset({x=0,y=0,z=0}, {x=0,y=0,z=0})
|
||||
elseif not self.driver then
|
||||
self.driver = clicker
|
||||
clicker:set_attach(self.object, "", self.attach_offset, {x=0,y=0,z=0})
|
||||
clicker:set_eye_offset(self.view_offset, self.view_offset)
|
||||
end
|
||||
end
|
||||
|
||||
function wagon:train()
|
||||
return advtrains.trains[self.train_id]
|
||||
end
|
||||
|
||||
function wagon:on_activate(staticdata, dtime_s)
|
||||
print("[advtrains][wagon "..(self.unique_id or "no-id").."] activated")
|
||||
self.object:set_armor_groups({immortal=1})
|
||||
if staticdata then
|
||||
local tmp = minetest.deserialize(staticdata)
|
||||
if tmp then
|
||||
self.unique_id=tmp.unique_id
|
||||
self.train_id=tmp.train_id
|
||||
self.wagon_flipped=tmp.wagon_flipped
|
||||
end
|
||||
|
||||
end
|
||||
self.old_pos = self.object:getpos()
|
||||
self.old_velocity = self.velocity
|
||||
self.initialized_pre=true
|
||||
|
||||
--same code is in on_step
|
||||
--does this object already have an ID?
|
||||
if not self.unique_id then
|
||||
self.unique_id=os.time()..os.clock()--should be random enough.
|
||||
end
|
||||
--is my train still here
|
||||
if not self.train_id or not self:train() then
|
||||
if self.initialized then
|
||||
print("[advtrains][wagon "..self.unique_id.."] missing train_id, destroying")
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
print("[advtrains][wagon "..self.unique_id.."] missing train_id, but not yet initialized, returning")
|
||||
return
|
||||
elseif not self.initialized then
|
||||
self.initialized=true
|
||||
end
|
||||
advtrains.update_trainpart_properties(self.train_id)
|
||||
end
|
||||
|
||||
function wagon:get_staticdata()
|
||||
return minetest.serialize({
|
||||
unique_id=self.unique_id,
|
||||
train_id=self.train_id,
|
||||
wagon_flipped=self.wagon_flipped,
|
||||
})
|
||||
end
|
||||
|
||||
-- Remove the wagon
|
||||
function wagon:on_punch(puncher, time_from_last_punch, tool_capabilities, direction)
|
||||
if not puncher or not puncher:is_player() then
|
||||
return
|
||||
end
|
||||
|
||||
self.object:remove()
|
||||
if not self.initialized then return end
|
||||
|
||||
local inv = puncher:get_inventory()
|
||||
if minetest.setting_getbool("creative_mode") then
|
||||
if not inv:contains_item("main", "advtrains:locomotive") then
|
||||
inv:add_item("main", "advtrains:locomotive")
|
||||
end
|
||||
else
|
||||
inv:add_item("main", "advtrains:locomotive")
|
||||
end
|
||||
|
||||
table.remove(self:train().trainparts, self.pos_in_trainparts)
|
||||
advtrains.update_trainpart_properties(self.train_id)
|
||||
return
|
||||
|
||||
|
||||
end
|
||||
|
||||
function wagon:on_step(dtime)
|
||||
local pos = self.object:getpos()
|
||||
if not self.initialized_pre then
|
||||
print("[advtrains] wagon stepping while not yet initialized_pre, returning")
|
||||
self.object:setvelocity({x=0,y=0,z=0})
|
||||
return
|
||||
end
|
||||
|
||||
--does this object already have an ID?
|
||||
if not self.unique_id then
|
||||
self.unique_id=os.time()..os.clock()--should be random enough.
|
||||
end
|
||||
--is my train still here
|
||||
if not self.train_id or not self:train() then
|
||||
if self.initialized then
|
||||
print("[advtrains][wagon "..self.unique_id.."] missing train_id, destroying")
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
print("[advtrains][wagon "..self.unique_id.."] missing train_id, but not yet initialized, returning")
|
||||
return
|
||||
elseif not self.initialized then
|
||||
self.initialized=true
|
||||
end
|
||||
|
||||
--driver control
|
||||
if self.driver and self.is_locomotive then
|
||||
if self.driver:get_player_control_bits()~=self.old_player_control_bits then
|
||||
local pc=self.driver:get_player_control()
|
||||
if pc.sneak then --stop
|
||||
self:train().tarvelocity=0
|
||||
elseif (not self.wagon_flipped and pc.up) or (self.wagon_flipped and pc.down) then --faster
|
||||
self:train().tarvelocity=math.min(self:train().tarvelocity+1, advtrains.all_traintypes[self:train().traintype].max_speed or 10)
|
||||
elseif (not self.wagon_flipped and pc.down) or (self.wagon_flipped and pc.up) then --slower
|
||||
self:train().tarvelocity=math.max(self:train().tarvelocity-1, -(advtrains.all_traintypes[self:train().traintype].max_speed or 10))
|
||||
elseif pc.aux1 then --slower
|
||||
if math.abs(self:train().velocity)<=3 then
|
||||
self.driver:set_detach()
|
||||
self.driver:set_eye_offset({x=0,y=0,z=0}, {x=0,y=0,z=0})
|
||||
advtrains.set_trainhud(self.driver:get_player_name(), "")
|
||||
self.driver = nil
|
||||
return--(don't let it crash because of statement below)
|
||||
else
|
||||
minetest.chat_send_player(self.driver:get_player_name(), "Can't get off driving train!")
|
||||
end
|
||||
end
|
||||
self.old_player_control_bits=self.driver:get_player_control_bits()
|
||||
end
|
||||
advtrains.set_trainhud(self.driver:get_player_name(), advtrains.hud_train_format(self:train(), self.wagon_flipped))
|
||||
end
|
||||
|
||||
local gp=self:train()
|
||||
--for path to be available. if not, skip step
|
||||
if not advtrains.get_or_create_path(self.train_id, gp) then
|
||||
self.object:setvelocity({x=0, y=0, z=0})
|
||||
return
|
||||
end
|
||||
|
||||
local pos_in_train_left=self.pos_in_train+0
|
||||
local index=gp.index
|
||||
if pos_in_train_left>(index-math.floor(index))*(gp.path_dist[math.floor(index)] or 1) then
|
||||
pos_in_train_left=pos_in_train_left - (index-math.floor(index))*(gp.path_dist[math.floor(index)] or 1)
|
||||
index=math.floor(index)
|
||||
while pos_in_train_left>(gp.path_dist[index-1] or 1) do
|
||||
pos_in_train_left=pos_in_train_left - (gp.path_dist[index-1] or 1)
|
||||
index=index-1
|
||||
end
|
||||
index=index-(pos_in_train_left/(gp.path_dist[index-1] or 1))
|
||||
else
|
||||
index=index-(pos_in_train_left*(gp.path_dist[math.floor(index-1)] or 1))
|
||||
end
|
||||
--print("trainindex "..gp.index.." wagonindex "..index)
|
||||
|
||||
--position recalculation
|
||||
local first_pos=gp.path[math.floor(index)]
|
||||
local second_pos=gp.path[math.floor(index)+1]
|
||||
if not first_pos or not second_pos then
|
||||
--print("[advtrains] object "..self.unique_id.." path end reached!")
|
||||
self.object:setvelocity({x=0,y=0,z=0})
|
||||
return
|
||||
end
|
||||
|
||||
--checking for environment collisions(a 3x3 cube around the center)
|
||||
if not gp.recently_collided_with_env then
|
||||
local collides=false
|
||||
for x=-1,1 do
|
||||
for y=0,2 do
|
||||
for z=-1,1 do
|
||||
local node=minetest.get_node_or_nil(vector.add(first_pos, {x=x, y=y, z=z}))
|
||||
if node and minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].walkable then
|
||||
collides=true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if collides then
|
||||
gp.recently_collided_with_env=true
|
||||
gp.velocity=-0.5*gp.velocity
|
||||
gp.tarvelocity=0
|
||||
end
|
||||
end
|
||||
|
||||
local velocity=gp.velocity/(gp.path_dist[math.floor(gp.index)] or 1)
|
||||
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,}
|
||||
local velocityvec={x=(first_pos.x-second_pos.x)*velocity*-1, z=(first_pos.z-second_pos.z)*velocity*-1, y=(first_pos.y-second_pos.y)*velocity*-1}
|
||||
|
||||
--some additional positions to determine orientation
|
||||
local aposfwd=gp.path[math.floor(index+2)]
|
||||
local aposbwd=gp.path[math.floor(index-1)]
|
||||
|
||||
local yaw
|
||||
if aposfwd and aposbwd then
|
||||
yaw=advtrains.get_wagon_yaw(aposfwd, second_pos, first_pos, aposbwd, factor)+math.pi--TODO remove when cleaning up
|
||||
else
|
||||
yaw=math.atan2((first_pos.x-second_pos.x), (second_pos.z-first_pos.z))
|
||||
end
|
||||
if self.wagon_flipped then
|
||||
yaw=yaw+math.pi
|
||||
end
|
||||
|
||||
self.updatepct_timer=(self.updatepct_timer or 0)-dtime
|
||||
if true or not self.old_velocity_vector or not vector.equals(velocityvec, self.old_velocity_vector) or self.old_yaw~=yaw or self.updatepct_timer<=0 then--only send update packet if something changed
|
||||
self.object:setpos(actual_pos)
|
||||
self.object:setvelocity(velocityvec)
|
||||
self.object:setyaw(yaw)
|
||||
self.updatepct_timer=2
|
||||
end
|
||||
|
||||
self.old_velocity_vector=velocityvec
|
||||
self.old_yaw=yaw
|
||||
end
|
||||
|
||||
|
||||
function advtrains.register_wagon(sysname, traintype, prototype)
|
||||
setmetatable(prototype, {__index=wagon})
|
||||
minetest.register_entity("advtrains:"..sysname,prototype)
|
||||
|
||||
minetest.register_craftitem("advtrains:"..sysname, {
|
||||
description = sysname,
|
||||
inventory_image = prototype.textures[1],
|
||||
wield_image = prototype.textures[1],
|
||||
stack_max = 1,
|
||||
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
if not pointed_thing.type == "node" then
|
||||
return
|
||||
end
|
||||
local le=minetest.env:add_entity(pointed_thing.under, "advtrains:"..sysname):get_luaentity()
|
||||
|
||||
local node=minetest.env:get_node_or_nil(pointed_thing.under)
|
||||
if not node then print("[advtrains]Ignore at placer position") return itemstack end
|
||||
local nodename=node.name
|
||||
if(not advtrains.is_track_and_drives_on(nodename, advtrains.all_traintypes[traintype].drives_on)) then
|
||||
print("[advtrains]no trck here, not placing.")
|
||||
return itemstack
|
||||
end
|
||||
local conn1=advtrains.get_track_connections(node.name, node.param2)
|
||||
local id=advtrains.create_new_train_at(pointed_thing.under, advtrains.dirCoordSet(pointed_thing.under, conn1), traintype)
|
||||
advtrains.add_wagon_to_train(le, id)
|
||||
print(dump(advtrains.trains))
|
||||
if not minetest.setting_getbool("creative_mode") then
|
||||
itemstack:take_item()
|
||||
end
|
||||
return itemstack
|
||||
|
||||
end,
|
||||
})
|
||||
end
|
||||
advtrains.register_train_type("steam", {"regular"})
|
||||
|
||||
--[[advtrains.register_wagon("blackwagon", "steam",{textures = {"black.png"}})
|
||||
advtrains.register_wagon("bluewagon", "steam",{textures = {"blue.png"}})
|
||||
advtrains.register_wagon("greenwagon", "steam",{textures = {"green.png"}})
|
||||
advtrains.register_wagon("redwagon", "steam",{textures = {"red.png"}})
|
||||
advtrains.register_wagon("yellowwagon", "steam",{textures = {"yellow.png"}})
|
||||
]]
|
||||
advtrains.register_wagon("newlocomotive", "steam",{
|
||||
mesh="newlocomotive.b3d",
|
||||
textures = {"advtrains_newlocomotive.png"},
|
||||
is_locomotive=true,
|
||||
attach_offset={x=5, y=10, z=-10},
|
||||
view_offset={x=0, y=6, z=18},
|
||||
visual_size = {x=1, y=1},
|
||||
wagon_span=1.85,
|
||||
collisionbox = {-1.0,-0.5,-1.0, 1.0,2.5,1.0},
|
||||
})
|
||||
advtrains.register_wagon("wagon_default", "steam",{
|
||||
mesh="wagon.b3d",
|
||||
textures = {"advtrains_wagon.png"},
|
||||
attach_offset={x=0, y=10, z=0},
|
||||
view_offset={x=0, y=6, z=0},
|
||||
visual_size = {x=1, y=1},
|
||||
wagon_span=1.8,
|
||||
collisionbox = {-1.0,-0.5,-1.0, 1.0,2.5,1.0},
|
||||
})
|
||||
|
||||
--[[
|
||||
advtrains.register_wagon("wagontype1",{on_rightclick=function(self, clicker)
|
||||
if clicker:get_player_control().sneak then
|
||||
advtrains.disconnect_train_before_wagon(self)
|
||||
return
|
||||
end
|
||||
--just debugging. look for first active wagon and attach to it.
|
||||
for _,v in pairs(minetest.luaentities) do
|
||||
if v.is_wagon and v.unique_id and v.unique_id~=self.unique_id then
|
||||
self.train_id=v.unique_id
|
||||
end
|
||||
end
|
||||
if not self.train_id then minetest.chat_send_all("not found") return end
|
||||
minetest.chat_send_all(self.train_id.." found and attached.")
|
||||
end})
|
||||
]]
|
||||
|
||||
|
|
@ -0,0 +1,336 @@
|
|||
--atan2 counts angles clockwise, minetest does counterclockwise
|
||||
local print=function(t) minetest.log("action", t) minetest.chat_send_all(t) end
|
||||
|
||||
local wagon={
|
||||
collisionbox = {-0.5,-0.5,-0.5, 0.5,0.5,0.5},
|
||||
--physical = true,
|
||||
visual = "mesh",
|
||||
mesh = "wagon.b3d",
|
||||
visual_size = {x=3, y=3},
|
||||
textures = {"black.png"},
|
||||
is_wagon=true,
|
||||
wagon_span=1,--how many index units of space does this wagon consume
|
||||
attach_offset={x=0, y=0, z=0},
|
||||
view_offset={x=0, y=0, z=0},
|
||||
}
|
||||
|
||||
|
||||
|
||||
function wagon:on_rightclick(clicker)
|
||||
print("[advtrains] wagon rightclick")
|
||||
if not clicker or not clicker:is_player() then
|
||||
return
|
||||
end
|
||||
if not self.initialized then
|
||||
print("[advtrains] not initiaalized")
|
||||
return
|
||||
end
|
||||
if clicker:get_player_control().sneak then
|
||||
advtrains.split_train_at_wagon(self)
|
||||
return
|
||||
end
|
||||
if clicker:get_player_control().aux1 then
|
||||
--advtrains.dumppath(self:train().path)
|
||||
--minetest.chat_send_all("at index "..(self:train().index or "nil"))
|
||||
--advtrains.invert_train(self.train_id)
|
||||
minetest.chat_send_all(dump(self:train()))
|
||||
return
|
||||
end
|
||||
if self.driver and clicker == self.driver then
|
||||
advtrains.set_trainhud(self.driver:get_player_name(), "")
|
||||
self.driver = nil
|
||||
clicker:set_detach()
|
||||
clicker:set_eye_offset({x=0,y=0,z=0}, {x=0,y=0,z=0})
|
||||
elseif not self.driver then
|
||||
self.driver = clicker
|
||||
clicker:set_attach(self.object, "", self.attach_offset, {x=0,y=0,z=0})
|
||||
clicker:set_eye_offset(self.view_offset, self.view_offset)
|
||||
end
|
||||
end
|
||||
|
||||
function wagon:train()
|
||||
return advtrains.trains[self.train_id]
|
||||
end
|
||||
|
||||
function wagon:on_activate(staticdata, dtime_s)
|
||||
print("[advtrains][wagon "..(self.unique_id or "no-id").."] activated")
|
||||
self.object:set_armor_groups({immortal=1})
|
||||
if staticdata then
|
||||
local tmp = minetest.deserialize(staticdata)
|
||||
if tmp then
|
||||
self.unique_id=tmp.unique_id
|
||||
self.train_id=tmp.train_id
|
||||
end
|
||||
|
||||
end
|
||||
self.old_pos = self.object:getpos()
|
||||
self.old_velocity = self.velocity
|
||||
self.initialized_pre=true
|
||||
|
||||
--same code is in on_step
|
||||
--does this object already have an ID?
|
||||
if not self.unique_id then
|
||||
self.unique_id=os.time()..os.clock()--should be random enough.
|
||||
end
|
||||
--is my train still here
|
||||
if not self.train_id or not self:train() then
|
||||
if self.initialized then
|
||||
print("[advtrains][wagon "..self.unique_id.."] missing train_id, destroying")
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
print("[advtrains][wagon "..self.unique_id.."] missing train_id, but not yet initialized, returning")
|
||||
return
|
||||
elseif not self.initialized then
|
||||
self.initialized=true
|
||||
end
|
||||
advtrains.update_trainpart_properties(self.train_id)
|
||||
end
|
||||
|
||||
function wagon:get_staticdata()
|
||||
return minetest.serialize({
|
||||
unique_id=self.unique_id,
|
||||
train_id=self.train_id,
|
||||
})
|
||||
end
|
||||
|
||||
-- Remove the wagon
|
||||
function wagon:on_punch(puncher, time_from_last_punch, tool_capabilities, direction)
|
||||
if not puncher or not puncher:is_player() then
|
||||
return
|
||||
end
|
||||
|
||||
self.object:remove()
|
||||
if not self.initialized then return end
|
||||
|
||||
local inv = puncher:get_inventory()
|
||||
if minetest.setting_getbool("creative_mode") then
|
||||
if not inv:contains_item("main", "advtrains:locomotive") then
|
||||
inv:add_item("main", "advtrains:locomotive")
|
||||
end
|
||||
else
|
||||
inv:add_item("main", "advtrains:locomotive")
|
||||
end
|
||||
|
||||
table.remove(self:train().trainparts, self.pos_in_trainparts)
|
||||
advtrains.update_trainpart_properties(self.train_id)
|
||||
return
|
||||
|
||||
|
||||
end
|
||||
|
||||
function wagon:on_step(dtime)
|
||||
local pos = self.object:getpos()
|
||||
if not self.initialized_pre then
|
||||
print("[advtrains] wagon stepping while not yet initialized_pre, returning")
|
||||
self.object:setvelocity({x=0,y=0,z=0})
|
||||
return
|
||||
end
|
||||
|
||||
--does this object already have an ID?
|
||||
if not self.unique_id then
|
||||
self.unique_id=os.time()..os.clock()--should be random enough.
|
||||
end
|
||||
--is my train still here
|
||||
if not self.train_id or not self:train() then
|
||||
if self.initialized then
|
||||
print("[advtrains][wagon "..self.unique_id.."] missing train_id, destroying")
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
print("[advtrains][wagon "..self.unique_id.."] missing train_id, but not yet initialized, returning")
|
||||
return
|
||||
elseif not self.initialized then
|
||||
self.initialized=true
|
||||
end
|
||||
|
||||
--driver control
|
||||
if self.driver and self.is_locomotive then
|
||||
if self.driver:get_player_control_bits()~=self.old_player_control_bits then
|
||||
local pc=self.driver:get_player_control()
|
||||
if pc.sneak then --stop
|
||||
self:train().tarvelocity=0
|
||||
elseif (not self.wagon_flipped and pc.up) or (self.wagon_flipped and pc.down) then --faster
|
||||
self:train().tarvelocity=math.min(self:train().tarvelocity+1, advtrains.all_traintypes[self:train().traintype].max_speed or 10)
|
||||
elseif (not self.wagon_flipped and pc.down) or (self.wagon_flipped and pc.up) then --slower
|
||||
self:train().tarvelocity=math.max(self:train().tarvelocity-1, -(advtrains.all_traintypes[self:train().traintype].max_speed or 10))
|
||||
elseif pc.aux1 then --slower
|
||||
if math.abs(self:train().velocity)<=3 then
|
||||
self.driver:set_detach()
|
||||
self.driver:set_eye_offset({x=0,y=0,z=0}, {x=0,y=0,z=0})
|
||||
advtrains.set_trainhud(self.driver:get_player_name(), "")
|
||||
self.driver = nil
|
||||
return--(don't let it crash because of statement below)
|
||||
else
|
||||
minetest.chat_send_player(self.driver:get_player_name(), "Can't get off driving train!")
|
||||
end
|
||||
end
|
||||
self.old_player_control_bits=self.driver:get_player_control_bits()
|
||||
end
|
||||
advtrains.set_trainhud(self.driver:get_player_name(), advtrains.hud_train_format(self:train(), self.wagon_flipped))
|
||||
end
|
||||
|
||||
local gp=self:train()
|
||||
--for path to be available. if not, skip step
|
||||
if not advtrains.get_or_create_path(self.train_id, gp) then
|
||||
self.object:setvelocity({x=0, y=0, z=0})
|
||||
return
|
||||
end
|
||||
|
||||
local pos_in_train_left=self.pos_in_train+0
|
||||
local index=gp.index
|
||||
if pos_in_train_left>(index-math.floor(index))*(gp.path_dist[math.floor(index)] or 1) then
|
||||
pos_in_train_left=pos_in_train_left - (index-math.floor(index))*(gp.path_dist[math.floor(index)] or 1)
|
||||
index=math.floor(index)
|
||||
while pos_in_train_left>(gp.path_dist[index-1] or 1) do
|
||||
pos_in_train_left=pos_in_train_left - (gp.path_dist[index-1] or 1)
|
||||
index=index-1
|
||||
end
|
||||
index=index-(pos_in_train_left/(gp.path_dist[index-1] or 1))
|
||||
else
|
||||
index=index-(pos_in_train_left*(gp.path_dist[math.floor(index-1)] or 1))
|
||||
end
|
||||
--print("trainindex "..gp.index.." wagonindex "..index)
|
||||
|
||||
--position recalculation
|
||||
local first_pos=gp.path[math.floor(index)]
|
||||
local second_pos=gp.path[math.floor(index)+1]
|
||||
if not first_pos or not second_pos then
|
||||
--print("[advtrains] object "..self.unique_id.." path end reached!")
|
||||
self.object:setvelocity({x=0,y=0,z=0})
|
||||
return
|
||||
end
|
||||
|
||||
--checking for environment collisions(a 3x3 cube around the center)
|
||||
if not gp.recently_collided_with_env then
|
||||
local collides=false
|
||||
for x=-1,1 do
|
||||
for y=0,2 do
|
||||
for z=-1,1 do
|
||||
local node=minetest.get_node_or_nil(vector.add(first_pos, {x=x, y=y, z=z}))
|
||||
if node and minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].walkable then
|
||||
collides=true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if collides then
|
||||
gp.recently_collided_with_env=true
|
||||
gp.velocity=-0.5*gp.velocity
|
||||
gp.tarvelocity=0
|
||||
end
|
||||
end
|
||||
|
||||
local velocity=gp.velocity/(gp.path_dist[math.floor(gp.index)] or 1)
|
||||
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,}
|
||||
local velocityvec={x=(first_pos.x-second_pos.x)*velocity*-1, z=(first_pos.z-second_pos.z)*velocity*-1, y=(first_pos.y-second_pos.y)*velocity*-1}
|
||||
|
||||
--some additional positions to determine orientation
|
||||
local aposfwd=gp.path[math.floor(index+2)]
|
||||
local aposbwd=gp.path[math.floor(index-1)]
|
||||
|
||||
local yaw
|
||||
if aposfwd and aposbwd then
|
||||
yaw=advtrains.get_wagon_yaw(aposfwd, second_pos, first_pos, aposbwd, factor)+math.pi--TODO remove when cleaning up
|
||||
else
|
||||
yaw=math.atan2((first_pos.x-second_pos.x), (second_pos.z-first_pos.z))
|
||||
end
|
||||
if self.wagon_flipped then
|
||||
yaw=yaw+math.pi
|
||||
end
|
||||
|
||||
self.updatepct_timer=(self.updatepct_timer or 0)-dtime
|
||||
if true or not self.old_velocity_vector or not vector.equals(velocityvec, self.old_velocity_vector) or self.old_yaw~=yaw or self.updatepct_timer<=0 then--only send update packet if something changed
|
||||
self.object:setpos(actual_pos)
|
||||
self.object:setvelocity(velocityvec)
|
||||
self.object:setyaw(yaw)
|
||||
self.updatepct_timer=2
|
||||
end
|
||||
|
||||
self.old_velocity_vector=velocityvec
|
||||
self.old_yaw=yaw
|
||||
end
|
||||
|
||||
|
||||
function advtrains.register_wagon(sysname, traintype, prototype)
|
||||
setmetatable(prototype, {__index=wagon})
|
||||
minetest.register_entity("advtrains:"..sysname,prototype)
|
||||
|
||||
minetest.register_craftitem("advtrains:"..sysname, {
|
||||
description = sysname,
|
||||
inventory_image = prototype.textures[1],
|
||||
wield_image = prototype.textures[1],
|
||||
stack_max = 1,
|
||||
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
if not pointed_thing.type == "node" then
|
||||
return
|
||||
end
|
||||
local le=minetest.env:add_entity(pointed_thing.under, "advtrains:"..sysname):get_luaentity()
|
||||
|
||||
local node=minetest.env:get_node_or_nil(pointed_thing.under)
|
||||
if not node then print("[advtrains]Ignore at placer position") return itemstack end
|
||||
local nodename=node.name
|
||||
if(not advtrains.is_track_and_drives_on(nodename, advtrains.all_traintypes[traintype].drives_on)) then
|
||||
print("[advtrains]no trck here, not placing.")
|
||||
return itemstack
|
||||
end
|
||||
local conn1=advtrains.get_track_connections(node.name, node.param2)
|
||||
local id=advtrains.create_new_train_at(pointed_thing.under, advtrains.dirCoordSet(pointed_thing.under, conn1), traintype)
|
||||
advtrains.add_wagon_to_train(le, id)
|
||||
print(dump(advtrains.trains))
|
||||
if not minetest.setting_getbool("creative_mode") then
|
||||
itemstack:take_item()
|
||||
end
|
||||
return itemstack
|
||||
|
||||
end,
|
||||
})
|
||||
end
|
||||
advtrains.register_train_type("steam", {"regular"})
|
||||
|
||||
--[[advtrains.register_wagon("blackwagon", "steam",{textures = {"black.png"}})
|
||||
advtrains.register_wagon("bluewagon", "steam",{textures = {"blue.png"}})
|
||||
advtrains.register_wagon("greenwagon", "steam",{textures = {"green.png"}})
|
||||
advtrains.register_wagon("redwagon", "steam",{textures = {"red.png"}})
|
||||
advtrains.register_wagon("yellowwagon", "steam",{textures = {"yellow.png"}})
|
||||
]]
|
||||
advtrains.register_wagon("newlocomotive", "steam",{
|
||||
mesh="newlocomotive.b3d",
|
||||
textures = {"advtrains_newlocomotive.png"},
|
||||
is_locomotive=true,
|
||||
attach_offset={x=5, y=10, z=-10},
|
||||
view_offset={x=0, y=6, z=18},
|
||||
visual_size = {x=1, y=1},
|
||||
wagon_span=1.85,
|
||||
collisionbox = {-1.0,-0.5,-1.0, 1.0,2.5,1.0},
|
||||
})
|
||||
advtrains.register_wagon("wagon_default", "steam",{
|
||||
mesh="wagon.b3d",
|
||||
textures = {"advtrains_wagon.png"},
|
||||
attach_offset={x=0, y=10, z=0},
|
||||
view_offset={x=0, y=6, z=0},
|
||||
visual_size = {x=1, y=1},
|
||||
wagon_span=1.8,
|
||||
collisionbox = {-1.0,-0.5,-1.0, 1.0,2.5,1.0},
|
||||
})
|
||||
|
||||
--[[
|
||||
advtrains.register_wagon("wagontype1",{on_rightclick=function(self, clicker)
|
||||
if clicker:get_player_control().sneak then
|
||||
advtrains.disconnect_train_before_wagon(self)
|
||||
return
|
||||
end
|
||||
--just debugging. look for first active wagon and attach to it.
|
||||
for _,v in pairs(minetest.luaentities) do
|
||||
if v.is_wagon and v.unique_id and v.unique_id~=self.unique_id then
|
||||
self.train_id=v.unique_id
|
||||
end
|
||||
end
|
||||
if not self.train_id then minetest.chat_send_all("not found") return end
|
||||
minetest.chat_send_all(self.train_id.." found and attached.")
|
||||
end})
|
||||
]]
|
||||
|
||||
|