From 2c864564d25eb1b30a359f62a8dcf9508cf7f104 Mon Sep 17 00:00:00 2001 From: orwell96 Date: Sun, 29 May 2016 20:27:30 +0200 Subject: created git repository --- debugitems.lua | 138 +++++++++ debugitems.lua~ | 138 +++++++++ helpers.lua | 215 ++++++++++++++ helpers.lua~ | 215 ++++++++++++++ init.lua | 19 ++ init.lua~ | 19 ++ models/locomotive.b3d | Bin 0 -> 181112 bytes models/locomotive.blend | Bin 0 -> 459600 bytes models/magnet_track.blend | Bin 0 -> 516836 bytes models/newlocomotive.b3d | Bin 0 -> 181112 bytes models/newlocomotive.blend | Bin 0 -> 603376 bytes models/newlocomotive.blend1 | Bin 0 -> 603376 bytes models/newlocomotive_uvs.png | Bin 0 -> 175030 bytes models/newwagon.blend | Bin 0 -> 542188 bytes models/newwagon.blend1 | Bin 0 -> 550292 bytes models/newwagon.png | Bin 0 -> 108762 bytes models/trackplane.b3d | Bin 0 -> 262 bytes models/trackplane.blend | Bin 0 -> 462200 bytes models/trackvertical1.b3d | Bin 0 -> 629 bytes models/trackvertical1.blend | Bin 0 -> 453656 bytes models/trackvertical1.blend1 | Bin 0 -> 453344 bytes models/trackvertical1.png | Bin 0 -> 59146 bytes models/trackvertical2.b3d | Bin 0 -> 869 bytes models/trackvertical2.blend | Bin 0 -> 459216 bytes models/trackvertical2.png | Bin 0 -> 66755 bytes models/wagon.b3d | Bin 0 -> 107352 bytes pseudoload.lua | 121 ++++++++ pseudoload.lua~ | 121 ++++++++ readme.txt | 27 ++ readme.txt~ | 24 ++ textures/advtrains_newlocomotive.png | Bin 0 -> 1141133 bytes textures/advtrains_newlocomotive.xcf | Bin 0 -> 7462378 bytes textures/advtrains_trackworker.png | Bin 0 -> 328 bytes textures/advtrains_wagon.png | Bin 0 -> 1276341 bytes textures/black.png | Bin 0 -> 908 bytes textures/blue.png | Bin 0 -> 1200 bytes textures/drwho_screwdriver.png | Bin 0 -> 328 bytes textures/green.png | Bin 0 -> 1200 bytes textures/nope/track_curve.png | Bin 0 -> 527 bytes textures/nope/track_curve_45.png | Bin 0 -> 552 bytes textures/nope/track_std.png | Bin 0 -> 414 bytes textures/nope/track_std_45.png | Bin 0 -> 1378 bytes textures/nope/track_switch_l.png | Bin 0 -> 526 bytes textures/nope/track_switch_l_45.png | Bin 0 -> 1133 bytes textures/nope/track_switch_r.png | Bin 0 -> 491 bytes textures/nope/track_switch_r_45.png | Bin 0 -> 1140 bytes textures/red.png | Bin 0 -> 1199 bytes textures/track_cr.png | Bin 0 -> 1933 bytes textures/track_cr_45.png | Bin 0 -> 1945 bytes textures/track_curve_45.xcf | Bin 0 -> 13824 bytes textures/track_placer.png | Bin 0 -> 621 bytes textures/track_st.png | Bin 0 -> 621 bytes textures/track_st_45.png | Bin 0 -> 2549 bytes textures/track_swl_cr.png | Bin 0 -> 1868 bytes textures/track_swl_cr_45.png | Bin 0 -> 2512 bytes textures/track_swl_st.png | Bin 0 -> 1892 bytes textures/track_swl_st_45.png | Bin 0 -> 2509 bytes textures/track_swr_cr.png | Bin 0 -> 1897 bytes textures/track_swr_cr_45.png | Bin 0 -> 2528 bytes textures/track_swr_st.png | Bin 0 -> 1912 bytes textures/track_swr_st_45.png | Bin 0 -> 2485 bytes textures/track_vert1.png | Bin 0 -> 1777 bytes textures/track_vert2.png | Bin 0 -> 2012 bytes textures/yellow.png | Bin 0 -> 1199 bytes trackplacer.lua | 200 +++++++++++++ trackplacer.lua~ | 200 +++++++++++++ tracks.lua | 152 ++++++++++ tracks.lua~ | 150 ++++++++++ trainhud.lua | 56 ++++ trainhud.lua~ | 54 ++++ trainlogic.lua | 543 +++++++++++++++++++++++++++++++++++ trainlogic.lua~ | 542 ++++++++++++++++++++++++++++++++++ wagons.lua | 338 ++++++++++++++++++++++ wagons.lua~ | 336 ++++++++++++++++++++++ 74 files changed, 3608 insertions(+) create mode 100644 debugitems.lua create mode 100644 debugitems.lua~ create mode 100644 helpers.lua create mode 100644 helpers.lua~ create mode 100644 init.lua create mode 100644 init.lua~ create mode 100644 models/locomotive.b3d create mode 100644 models/locomotive.blend create mode 100644 models/magnet_track.blend create mode 100644 models/newlocomotive.b3d create mode 100644 models/newlocomotive.blend create mode 100644 models/newlocomotive.blend1 create mode 100644 models/newlocomotive_uvs.png create mode 100644 models/newwagon.blend create mode 100644 models/newwagon.blend1 create mode 100644 models/newwagon.png create mode 100644 models/trackplane.b3d create mode 100644 models/trackplane.blend create mode 100644 models/trackvertical1.b3d create mode 100644 models/trackvertical1.blend create mode 100644 models/trackvertical1.blend1 create mode 100644 models/trackvertical1.png create mode 100644 models/trackvertical2.b3d create mode 100644 models/trackvertical2.blend create mode 100644 models/trackvertical2.png create mode 100644 models/wagon.b3d create mode 100644 pseudoload.lua create mode 100644 pseudoload.lua~ create mode 100644 readme.txt create mode 100644 readme.txt~ create mode 100644 textures/advtrains_newlocomotive.png create mode 100644 textures/advtrains_newlocomotive.xcf create mode 100644 textures/advtrains_trackworker.png create mode 100644 textures/advtrains_wagon.png create mode 100644 textures/black.png create mode 100644 textures/blue.png create mode 100644 textures/drwho_screwdriver.png create mode 100644 textures/green.png create mode 100644 textures/nope/track_curve.png create mode 100644 textures/nope/track_curve_45.png create mode 100644 textures/nope/track_std.png create mode 100644 textures/nope/track_std_45.png create mode 100644 textures/nope/track_switch_l.png create mode 100644 textures/nope/track_switch_l_45.png create mode 100644 textures/nope/track_switch_r.png create mode 100644 textures/nope/track_switch_r_45.png create mode 100644 textures/red.png create mode 100644 textures/track_cr.png create mode 100644 textures/track_cr_45.png create mode 100644 textures/track_curve_45.xcf create mode 100644 textures/track_placer.png create mode 100644 textures/track_st.png create mode 100644 textures/track_st_45.png create mode 100644 textures/track_swl_cr.png create mode 100644 textures/track_swl_cr_45.png create mode 100644 textures/track_swl_st.png create mode 100644 textures/track_swl_st_45.png create mode 100644 textures/track_swr_cr.png create mode 100644 textures/track_swr_cr_45.png create mode 100644 textures/track_swr_st.png create mode 100644 textures/track_swr_st_45.png create mode 100644 textures/track_vert1.png create mode 100644 textures/track_vert2.png create mode 100644 textures/yellow.png create mode 100644 trackplacer.lua create mode 100644 trackplacer.lua~ create mode 100644 tracks.lua create mode 100644 tracks.lua~ create mode 100644 trainhud.lua create mode 100644 trainhud.lua~ create mode 100644 trainlogic.lua create mode 100644 trainlogic.lua~ create mode 100644 wagons.lua create mode 100644 wagons.lua~ diff --git a/debugitems.lua b/debugitems.lua new file mode 100644 index 0000000..89399d2 --- /dev/null +++ b/debugitems.lua @@ -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. +]] +} +) diff --git a/debugitems.lua~ b/debugitems.lua~ new file mode 100644 index 0000000..7de1a0f --- /dev/null +++ b/debugitems.lua~ @@ -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. +]] +} +) diff --git a/helpers.lua b/helpers.lua new file mode 100644 index 0000000..02367d8 --- /dev/null +++ b/helpers.lua @@ -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) 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 diff --git a/helpers.lua~ b/helpers.lua~ new file mode 100644 index 0000000..aa48584 --- /dev/null +++ b/helpers.lua~ @@ -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) 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 diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..bc3b366 --- /dev/null +++ b/init.lua @@ -0,0 +1,19 @@ +--advtrains + +advtrains={} + +advtrains.modpath = minetest.get_modpath("advtrains") + +print=function(text) + minetest.log("action", tostring(text) or "") +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") diff --git a/init.lua~ b/init.lua~ new file mode 100644 index 0000000..63856a5 --- /dev/null +++ b/init.lua~ @@ -0,0 +1,19 @@ +--advtrains + +advtrains={} + +advtrains.modpath = minetest.get_modpath("advtrains") + +print=function(text) + minetest.log("action", tostring(text) or "") +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") diff --git a/models/locomotive.b3d b/models/locomotive.b3d new file mode 100644 index 0000000..5e16be3 Binary files /dev/null and b/models/locomotive.b3d differ diff --git a/models/locomotive.blend b/models/locomotive.blend new file mode 100644 index 0000000..8fd6059 Binary files /dev/null and b/models/locomotive.blend differ diff --git a/models/magnet_track.blend b/models/magnet_track.blend new file mode 100644 index 0000000..0ab14e7 Binary files /dev/null and b/models/magnet_track.blend differ diff --git a/models/newlocomotive.b3d b/models/newlocomotive.b3d new file mode 100644 index 0000000..da36222 Binary files /dev/null and b/models/newlocomotive.b3d differ diff --git a/models/newlocomotive.blend b/models/newlocomotive.blend new file mode 100644 index 0000000..858882b Binary files /dev/null and b/models/newlocomotive.blend differ diff --git a/models/newlocomotive.blend1 b/models/newlocomotive.blend1 new file mode 100644 index 0000000..4b8b24f Binary files /dev/null and b/models/newlocomotive.blend1 differ diff --git a/models/newlocomotive_uvs.png b/models/newlocomotive_uvs.png new file mode 100644 index 0000000..879be65 Binary files /dev/null and b/models/newlocomotive_uvs.png differ diff --git a/models/newwagon.blend b/models/newwagon.blend new file mode 100644 index 0000000..1cedc55 Binary files /dev/null and b/models/newwagon.blend differ diff --git a/models/newwagon.blend1 b/models/newwagon.blend1 new file mode 100644 index 0000000..eb30fde Binary files /dev/null and b/models/newwagon.blend1 differ diff --git a/models/newwagon.png b/models/newwagon.png new file mode 100644 index 0000000..812bc32 Binary files /dev/null and b/models/newwagon.png differ diff --git a/models/trackplane.b3d b/models/trackplane.b3d new file mode 100644 index 0000000..b4728c3 Binary files /dev/null and b/models/trackplane.b3d differ diff --git a/models/trackplane.blend b/models/trackplane.blend new file mode 100644 index 0000000..79365f7 Binary files /dev/null and b/models/trackplane.blend differ diff --git a/models/trackvertical1.b3d b/models/trackvertical1.b3d new file mode 100644 index 0000000..5620f60 Binary files /dev/null and b/models/trackvertical1.b3d differ diff --git a/models/trackvertical1.blend b/models/trackvertical1.blend new file mode 100644 index 0000000..fca6742 Binary files /dev/null and b/models/trackvertical1.blend differ diff --git a/models/trackvertical1.blend1 b/models/trackvertical1.blend1 new file mode 100644 index 0000000..b146b56 Binary files /dev/null and b/models/trackvertical1.blend1 differ diff --git a/models/trackvertical1.png b/models/trackvertical1.png new file mode 100644 index 0000000..a998dcb Binary files /dev/null and b/models/trackvertical1.png differ diff --git a/models/trackvertical2.b3d b/models/trackvertical2.b3d new file mode 100644 index 0000000..22dbb20 Binary files /dev/null and b/models/trackvertical2.b3d differ diff --git a/models/trackvertical2.blend b/models/trackvertical2.blend new file mode 100644 index 0000000..a066b84 Binary files /dev/null and b/models/trackvertical2.blend differ diff --git a/models/trackvertical2.png b/models/trackvertical2.png new file mode 100644 index 0000000..43142f9 Binary files /dev/null and b/models/trackvertical2.png differ diff --git a/models/wagon.b3d b/models/wagon.b3d new file mode 100644 index 0000000..5c8214c Binary files /dev/null and b/models/wagon.b3d differ diff --git a/pseudoload.lua b/pseudoload.lua new file mode 100644 index 0000000..532b429 --- /dev/null +++ b/pseudoload.lua @@ -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 + + diff --git a/pseudoload.lua~ b/pseudoload.lua~ new file mode 100644 index 0000000..6b04dda --- /dev/null +++ b/pseudoload.lua~ @@ -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 + + diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..944fa95 --- /dev/null +++ b/readme.txt @@ -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! \ No newline at end of file diff --git a/readme.txt~ b/readme.txt~ new file mode 100644 index 0000000..2f0601e --- /dev/null +++ b/readme.txt~ @@ -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! \ No newline at end of file diff --git a/textures/advtrains_newlocomotive.png b/textures/advtrains_newlocomotive.png new file mode 100644 index 0000000..fcc1a3d Binary files /dev/null and b/textures/advtrains_newlocomotive.png differ diff --git a/textures/advtrains_newlocomotive.xcf b/textures/advtrains_newlocomotive.xcf new file mode 100644 index 0000000..9ad5187 Binary files /dev/null and b/textures/advtrains_newlocomotive.xcf differ diff --git a/textures/advtrains_trackworker.png b/textures/advtrains_trackworker.png new file mode 100644 index 0000000..b50bcae Binary files /dev/null and b/textures/advtrains_trackworker.png differ diff --git a/textures/advtrains_wagon.png b/textures/advtrains_wagon.png new file mode 100644 index 0000000..ee836a8 Binary files /dev/null and b/textures/advtrains_wagon.png differ diff --git a/textures/black.png b/textures/black.png new file mode 100644 index 0000000..a1d3eb7 Binary files /dev/null and b/textures/black.png differ diff --git a/textures/blue.png b/textures/blue.png new file mode 100644 index 0000000..b1a03a7 Binary files /dev/null and b/textures/blue.png differ diff --git a/textures/drwho_screwdriver.png b/textures/drwho_screwdriver.png new file mode 100644 index 0000000..b50bcae Binary files /dev/null and b/textures/drwho_screwdriver.png differ diff --git a/textures/green.png b/textures/green.png new file mode 100644 index 0000000..5717393 Binary files /dev/null and b/textures/green.png differ diff --git a/textures/nope/track_curve.png b/textures/nope/track_curve.png new file mode 100644 index 0000000..02bcf5c Binary files /dev/null and b/textures/nope/track_curve.png differ diff --git a/textures/nope/track_curve_45.png b/textures/nope/track_curve_45.png new file mode 100644 index 0000000..c69be2d Binary files /dev/null and b/textures/nope/track_curve_45.png differ diff --git a/textures/nope/track_std.png b/textures/nope/track_std.png new file mode 100644 index 0000000..061949e Binary files /dev/null and b/textures/nope/track_std.png differ diff --git a/textures/nope/track_std_45.png b/textures/nope/track_std_45.png new file mode 100644 index 0000000..89a1b36 Binary files /dev/null and b/textures/nope/track_std_45.png differ diff --git a/textures/nope/track_switch_l.png b/textures/nope/track_switch_l.png new file mode 100644 index 0000000..33ac21e Binary files /dev/null and b/textures/nope/track_switch_l.png differ diff --git a/textures/nope/track_switch_l_45.png b/textures/nope/track_switch_l_45.png new file mode 100644 index 0000000..d55da42 Binary files /dev/null and b/textures/nope/track_switch_l_45.png differ diff --git a/textures/nope/track_switch_r.png b/textures/nope/track_switch_r.png new file mode 100644 index 0000000..bf2e722 Binary files /dev/null and b/textures/nope/track_switch_r.png differ diff --git a/textures/nope/track_switch_r_45.png b/textures/nope/track_switch_r_45.png new file mode 100644 index 0000000..5be48fd Binary files /dev/null and b/textures/nope/track_switch_r_45.png differ diff --git a/textures/red.png b/textures/red.png new file mode 100644 index 0000000..ccf1c78 Binary files /dev/null and b/textures/red.png differ diff --git a/textures/track_cr.png b/textures/track_cr.png new file mode 100644 index 0000000..54ddf79 Binary files /dev/null and b/textures/track_cr.png differ diff --git a/textures/track_cr_45.png b/textures/track_cr_45.png new file mode 100644 index 0000000..ad4580e Binary files /dev/null and b/textures/track_cr_45.png differ diff --git a/textures/track_curve_45.xcf b/textures/track_curve_45.xcf new file mode 100644 index 0000000..0e6c26e Binary files /dev/null and b/textures/track_curve_45.xcf differ diff --git a/textures/track_placer.png b/textures/track_placer.png new file mode 100644 index 0000000..afed4ce Binary files /dev/null and b/textures/track_placer.png differ diff --git a/textures/track_st.png b/textures/track_st.png new file mode 100644 index 0000000..afed4ce Binary files /dev/null and b/textures/track_st.png differ diff --git a/textures/track_st_45.png b/textures/track_st_45.png new file mode 100644 index 0000000..e3ce89f Binary files /dev/null and b/textures/track_st_45.png differ diff --git a/textures/track_swl_cr.png b/textures/track_swl_cr.png new file mode 100644 index 0000000..602d727 Binary files /dev/null and b/textures/track_swl_cr.png differ diff --git a/textures/track_swl_cr_45.png b/textures/track_swl_cr_45.png new file mode 100644 index 0000000..f3b741a Binary files /dev/null and b/textures/track_swl_cr_45.png differ diff --git a/textures/track_swl_st.png b/textures/track_swl_st.png new file mode 100644 index 0000000..b12a9ac Binary files /dev/null and b/textures/track_swl_st.png differ diff --git a/textures/track_swl_st_45.png b/textures/track_swl_st_45.png new file mode 100644 index 0000000..cf80d77 Binary files /dev/null and b/textures/track_swl_st_45.png differ diff --git a/textures/track_swr_cr.png b/textures/track_swr_cr.png new file mode 100644 index 0000000..92a09bd Binary files /dev/null and b/textures/track_swr_cr.png differ diff --git a/textures/track_swr_cr_45.png b/textures/track_swr_cr_45.png new file mode 100644 index 0000000..f127e30 Binary files /dev/null and b/textures/track_swr_cr_45.png differ diff --git a/textures/track_swr_st.png b/textures/track_swr_st.png new file mode 100644 index 0000000..7d014fc Binary files /dev/null and b/textures/track_swr_st.png differ diff --git a/textures/track_swr_st_45.png b/textures/track_swr_st_45.png new file mode 100644 index 0000000..5d7764a Binary files /dev/null and b/textures/track_swr_st_45.png differ diff --git a/textures/track_vert1.png b/textures/track_vert1.png new file mode 100644 index 0000000..ce7c31d Binary files /dev/null and b/textures/track_vert1.png differ diff --git a/textures/track_vert2.png b/textures/track_vert2.png new file mode 100644 index 0000000..7bc7bae Binary files /dev/null and b/textures/track_vert2.png differ diff --git a/textures/yellow.png b/textures/yellow.png new file mode 100644 index 0000000..e2e5bc2 Binary files /dev/null and b/textures/yellow.png differ diff --git a/trackplacer.lua b/trackplacer.lua new file mode 100644 index 0000000..68c1c7c --- /dev/null +++ b/trackplacer.lua @@ -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, +}) \ No newline at end of file diff --git a/trackplacer.lua~ b/trackplacer.lua~ new file mode 100644 index 0000000..68c1c7c --- /dev/null +++ b/trackplacer.lua~ @@ -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, +}) \ No newline at end of file diff --git a/tracks.lua b/tracks.lua new file mode 100644 index 0000000..b164dd5 --- /dev/null +++ b/tracks.lua @@ -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 + + + + + + + diff --git a/tracks.lua~ b/tracks.lua~ new file mode 100644 index 0000000..b2513b2 --- /dev/null +++ b/tracks.lua~ @@ -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 + + + + + + + diff --git a/trainhud.lua b/trainhud.lua new file mode 100644 index 0000000..b64c81c --- /dev/null +++ b/trainhud.lua @@ -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 \ No newline at end of file diff --git a/trainhud.lua~ b/trainhud.lua~ new file mode 100644 index 0000000..7f60fe8 --- /dev/null +++ b/trainhud.lua~ @@ -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 \ No newline at end of file diff --git a/trainlogic.lua b/trainlogic.lua new file mode 100644 index 0000000..74e9c97 --- /dev/null +++ b/trainlogic.lua @@ -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)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_tracktrain.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 \ No newline at end of file diff --git a/trainlogic.lua~ b/trainlogic.lua~ new file mode 100644 index 0000000..9838f81 --- /dev/null +++ b/trainlogic.lua~ @@ -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)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_tracktrain.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 \ No newline at end of file diff --git a/wagons.lua b/wagons.lua new file mode 100644 index 0000000..7c634f0 --- /dev/null +++ b/wagons.lua @@ -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}) +]] + + diff --git a/wagons.lua~ b/wagons.lua~ new file mode 100644 index 0000000..1d08bf7 --- /dev/null +++ b/wagons.lua~ @@ -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}) +]] + + -- cgit v1.2.3