Merge nocrash branch into master and merging it with the unified functions so that restoring works how it should

Also fix minor bugs and an occasional crash with couples
master
orwell96 2017-04-29 19:44:43 +02:00
commit 0c7144bcc4
12 changed files with 829 additions and 758 deletions

Binary file not shown.

View File

@ -92,49 +92,53 @@ advtrains.register_tracks("default", {
return {
after_place_node=apn_func,
after_dig_node=function(pos)
advtrains.invalidate_all_paths()
advtrains.ndb.clear(pos)
local pts=minetest.pos_to_string(pos)
atc.controllers[pts]=nil
return advtrains.pcall(function()
advtrains.invalidate_all_paths()
advtrains.ndb.clear(pos)
local pts=minetest.pos_to_string(pos)
atc.controllers[pts]=nil
end)
end,
on_receive_fields = function(pos, formname, fields, player)
if advtrains.is_protected(pos, player:get_player_name()) then
minetest.record_protection_violation(pos, player:get_player_name())
return
end
local meta=minetest.get_meta(pos)
if meta then
if not fields.save then
--maybe only the dropdown changed
if fields.mode then
meta:set_string("mode", idxtrans[fields.mode])
if fields.mode=="digiline" then
meta:set_string("infotext", attrans("ATC controller, mode @1\nChannel: @2", fields.mode, meta:get_string("command")) )
else
meta:set_string("infotext", attrans("ATC controller, mode @1\nCommand: @2", fields.mode, meta:get_string("command")) )
end
meta:set_string("formspec", atc.get_atc_controller_formspec(pos, meta))
end
return advtrains.pcall(function()
if advtrains.is_protected(pos, player:get_player_name()) then
minetest.record_protection_violation(pos, player:get_player_name())
return
end
meta:set_string("mode", idxtrans[fields.mode])
meta:set_string("command", fields.command)
meta:set_string("command_on", fields.command_on)
meta:set_string("channel", fields.channel)
if fields.mode=="digiline" then
meta:set_string("infotext", attrans("ATC controller, mode @1\nChannel: @2", fields.mode, meta:get_string("command")) )
else
meta:set_string("infotext", attrans("ATC controller, mode @1\nCommand: @2", fields.mode, meta:get_string("command")) )
local meta=minetest.get_meta(pos)
if meta then
if not fields.save then
--maybe only the dropdown changed
if fields.mode then
meta:set_string("mode", idxtrans[fields.mode])
if fields.mode=="digiline" then
meta:set_string("infotext", attrans("ATC controller, mode @1\nChannel: @2", fields.mode, meta:get_string("command")) )
else
meta:set_string("infotext", attrans("ATC controller, mode @1\nCommand: @2", fields.mode, meta:get_string("command")) )
end
meta:set_string("formspec", atc.get_atc_controller_formspec(pos, meta))
end
return
end
meta:set_string("mode", idxtrans[fields.mode])
meta:set_string("command", fields.command)
meta:set_string("command_on", fields.command_on)
meta:set_string("channel", fields.channel)
if fields.mode=="digiline" then
meta:set_string("infotext", attrans("ATC controller, mode @1\nChannel: @2", fields.mode, meta:get_string("command")) )
else
meta:set_string("infotext", attrans("ATC controller, mode @1\nCommand: @2", fields.mode, meta:get_string("command")) )
end
meta:set_string("formspec", atc.get_atc_controller_formspec(pos, meta))
local pts=minetest.pos_to_string(pos)
local _, conn1=advtrains.get_rail_info_at(pos, advtrains.all_tracktypes)
atc.controllers[pts]={command=fields.command, arrowconn=conn1}
if advtrains.detector.on_node[pts] then
atc.send_command(pos)
end
end
meta:set_string("formspec", atc.get_atc_controller_formspec(pos, meta))
local pts=minetest.pos_to_string(pos)
local _, conn1=advtrains.get_rail_info_at(pos, advtrains.all_tracktypes)
atc.controllers[pts]={command=fields.command, arrowconn=conn1}
if advtrains.detector.on_node[pts] then
atc.send_command(pos)
end
end
end)
end,
advtrains = {
on_train_enter = function(pos, train_id)

View File

@ -31,52 +31,56 @@ minetest.register_entity("advtrains:discouple", {
end,
get_staticdata=function() return "DISCOUPLE" end,
on_punch=function(self, player)
--only if player owns at least one wagon next to this
local own=player:get_player_name()
if self.wagon.owner and self.wagon.owner==own and not self.wagon.lock_couples then
local train=advtrains.trains[self.wagon.train_id]
local nextwgn_id=train.trainparts[self.wagon.pos_in_trainparts-1]
for aoi, le in pairs(minetest.luaentities) do
if le and le.is_wagon then
if le.unique_id==nextwgn_id then
if le.owner and le.owner~=own then
minetest.chat_send_player(own, attrans("You need to own at least one neighboring wagon to destroy this couple."))
return
return advtrains.pcall(function()
--only if player owns at least one wagon next to this
local own=player:get_player_name()
if self.wagon.owner and self.wagon.owner==own and not self.wagon.lock_couples then
local train=advtrains.trains[self.wagon.train_id]
local nextwgn_id=train.trainparts[self.wagon.pos_in_trainparts-1]
for aoi, le in pairs(minetest.luaentities) do
if le and le.is_wagon then
if le.unique_id==nextwgn_id then
if le.owner and le.owner~=own then
minetest.chat_send_player(own, attrans("You need to own at least one neighboring wagon to destroy this couple."))
return
end
end
end
end
atprint("Discouple punched. Splitting train", self.wagon.train_id)
advtrains.split_train_at_wagon(self.wagon)--found in trainlogic.lua
self.object:remove()
elseif self.wagon.lock_couples then
minetest.chat_send_player(own, "Couples of one of the wagons are locked, can't discouple!")
else
minetest.chat_send_player(own, attrans("You need to own at least one neighboring wagon to destroy this couple."))
end
atprint("Discouple punched. Splitting train", self.wagon.train_id)
advtrains.split_train_at_wagon(self.wagon)--found in trainlogic.lua
self.object:remove()
elseif self.wagon.lock_couples then
minetest.chat_send_player(own, "Couples of one of the wagons are locked, can't discouple!")
else
minetest.chat_send_player(own, attrans("You need to own at least one neighboring wagon to destroy this couple."))
end
end)
end,
on_step=function(self, dtime)
local t=os.clock()
if not self.wagon then
self.object:remove()
atprint("Discouple: no wagon, destroying")
return
end
--getyaw seems to be a reliable method to check if an object is loaded...if it returns nil, it is not.
if not self.wagon.object:getyaw() then
atprint("Discouple: wagon no longer loaded, destroying")
self.object:remove()
return
end
local velocityvec=self.wagon.object:getvelocity()
self.updatepct_timer=(self.updatepct_timer or 0)-dtime
if not self.old_velocity_vector or not vector.equals(velocityvec, self.old_velocity_vector) or self.updatepct_timer<=0 then--only send update packet if something changed
local flipsign=self.wagon.wagon_flipped and -1 or 1
self.object:setpos(vector.add(self.wagon.object:getpos(), {y=0, x=-math.sin(self.wagon.object:getyaw())*self.wagon.wagon_span*flipsign, z=math.cos(self.wagon.object:getyaw())*self.wagon.wagon_span*flipsign}))
self.object:setvelocity(velocityvec)
self.updatepct_timer=2
end
atprintbm("discouple_step", t)
return advtrains.pcall(function()
local t=os.clock()
if not self.wagon then
self.object:remove()
atprint("Discouple: no wagon, destroying")
return
end
--getyaw seems to be a reliable method to check if an object is loaded...if it returns nil, it is not.
if not self.wagon.object:getyaw() then
atprint("Discouple: wagon no longer loaded, destroying")
self.object:remove()
return
end
local velocityvec=self.wagon.object:getvelocity()
self.updatepct_timer=(self.updatepct_timer or 0)-dtime
if not self.old_velocity_vector or not vector.equals(velocityvec, self.old_velocity_vector) or self.updatepct_timer<=0 then--only send update packet if something changed
local flipsign=self.wagon.wagon_flipped and -1 or 1
self.object:setpos(vector.add(self.wagon.object:getpos(), {y=0, x=-math.sin(self.wagon.object:getyaw())*self.wagon.wagon_span*flipsign, z=math.cos(self.wagon.object:getyaw())*self.wagon.wagon_span*flipsign}))
self.object:setvelocity(velocityvec)
self.updatepct_timer=2
end
atprintbm("discouple_step", t)
end)
end,
})
@ -98,74 +102,79 @@ minetest.register_entity("advtrains:couple", {
initial_sprite_basepos = {x=0, y=0},
is_couple=true,
on_activate=function(self, staticdata)
if staticdata=="COUPLE" then
--couple entities have no right to exist further...
atprint("Couple loaded from staticdata, destroying")
self.object:remove()
return
end
on_activate=function(self, staticdata)
return advtrains.pcall(function()
if staticdata=="COUPLE" then
--couple entities have no right to exist further...
atprint("Couple loaded from staticdata, destroying")
self.object:remove()
return
end
end)
end,
get_staticdata=function(self) return "COUPLE" end,
on_rightclick=function(self, clicker)
if not self.train_id_1 or not self.train_id_2 then return end
local id1, id2=self.train_id_1, self.train_id_2
if self.train1_is_backpos and not self.train2_is_backpos then
advtrains.do_connect_trains(id1, id2, clicker)
--case 2 (second train is front)
elseif self.train2_is_backpos and not self.train1_is_backpos then
advtrains.do_connect_trains(id2, id1, clicker)
--case 3
elseif self.train1_is_backpos and self.train2_is_backpos then
advtrains.invert_train(id2)
advtrains.do_connect_trains(id1, id2, clicker)
--case 4
elseif not self.train1_is_backpos and not self.train2_is_backpos then
advtrains.invert_train(id1)
advtrains.do_connect_trains(id1, id2, clicker)
end
atprint("Coupled trains", id1, id2)
self.object:remove()
return advtrains.pcall(function()
if not self.train_id_1 or not self.train_id_2 then return end
local id1, id2=self.train_id_1, self.train_id_2
if self.train1_is_backpos and not self.train2_is_backpos then
advtrains.do_connect_trains(id1, id2, clicker)
--case 2 (second train is front)
elseif self.train2_is_backpos and not self.train1_is_backpos then
advtrains.do_connect_trains(id2, id1, clicker)
--case 3
elseif self.train1_is_backpos and self.train2_is_backpos then
advtrains.invert_train(id2)
advtrains.do_connect_trains(id1, id2, clicker)
--case 4
elseif not self.train1_is_backpos and not self.train2_is_backpos then
advtrains.invert_train(id1)
advtrains.do_connect_trains(id1, id2, clicker)
end
atprint("Coupled trains", id1, id2)
self.object:remove()
end)
end,
on_step=function(self, dtime)
local t=os.clock()
if not self.train_id_1 or not self.train_id_2 then atprint("Couple: train ids not set!") self.object:remove() return end
local train1=advtrains.trains[self.train_id_1]
local train2=advtrains.trains[self.train_id_2]
if not train1 or not train2 then
atprint("Couple: trains missing, destroying")
self.object:remove()
return
end
if not train1.path or not train2.path or not train1.index or not train2.index or not train1.end_index or not train2.end_index then
atprint("Couple: paths or end_index missing. Might happen when paths got cleared")
return
end
local tp1
if not self.train1_is_backpos then
tp1=advtrains.get_real_index_position(train1.path, train1.index)
else
tp1=advtrains.get_real_index_position(train1.path, train1.end_index)
end
local tp2
if not self.train2_is_backpos then
tp2=advtrains.get_real_index_position(train2.path, train2.index)
else
tp2=advtrains.get_real_index_position(train2.path, train2.end_index)
end
if not tp1 or not tp2 or not (vector.distance(tp1,tp2)<couple_max_dist) then
atprint("Couple: train end positions too distanced, destroying")
self.object:remove()
return
else
local pos_median=advtrains.pos_median(tp1, tp2)
if not vector.equals(pos_median, self.object:getpos()) then
self.object:setpos(pos_median)
return advtrains.pcall(function()
local t=os.clock()
if not self.train_id_1 or not self.train_id_2 then atprint("Couple: train ids not set!") self.object:remove() return end
local train1=advtrains.trains[self.train_id_1]
local train2=advtrains.trains[self.train_id_2]
if not train1 or not train2 then
atprint("Couple: trains missing, destroying")
self.object:remove()
return
end
end
atprintbm("couple step", t)
if not train1.path or not train2.path or not train1.index or not train2.index or not train1.end_index or not train2.end_index then
atprint("Couple: paths or end_index missing. Might happen when paths got cleared")
return
end
local tp1
if not self.train1_is_backpos then
tp1=advtrains.get_real_index_position(train1.path, train1.index)
else
tp1=advtrains.get_real_index_position(train1.path, train1.end_index)
end
local tp2
if not self.train2_is_backpos then
tp2=advtrains.get_real_index_position(train2.path, train2.index)
else
tp2=advtrains.get_real_index_position(train2.path, train2.end_index)
end
if not tp1 or not tp2 or not (vector.distance(tp1,tp2)<couple_max_dist) then
atprint("Couple: train end positions too distanced, destroying")
self.object:remove()
return
else
local pos_median=advtrains.pos_median(tp1, tp2)
if not vector.equals(pos_median, self.object:getpos()) then
self.object:setpos(pos_median)
end
end
atprintbm("couple step", t)
end)
end,
})

View File

@ -9,6 +9,29 @@ end
advtrains = {trains={}, wagon_save={}, player_to_train_mapping={}}
--pcall
local no_action=false
function advtrains.pcall(fun)
if no_action then return end
local succ, err, return1, return2, return3, return4=pcall(fun)
if not succ then
atwarn("Lua Error occured: ", err)
atwarn("Restoring saved state in 1 second...")
no_action=true
--read last save state and continue, as if server was restarted
for aoi, le in pairs(minetest.luaentities) do
if le.is_wagon then
le.object:remove()
end
end
minetest.after(1, advtrains.load)
else
return err, return1, return2, return3, return4
end
end
advtrains.modpath = minetest.get_modpath("advtrains")
function advtrains.print_concat_table(a)
@ -94,6 +117,7 @@ dofile(advtrains.modpath.."/craft_items.lua")
--load/save
advtrains.fpath=minetest.get_worldpath().."/advtrains"
function advtrains.avt_load()
local file, err = io.open(advtrains.fpath, "r")
if not file then
@ -197,44 +221,47 @@ end
--## MAIN LOOP ##--
--Calls all subsequent main tasks of both advtrains and atlatc
local init_load
local init_load=false
local save_interval=20
local save_timer=save_interval
minetest.register_globalstep(function(dtime_mt)
--call load once. see advtrains.load() comment
if not init_load then
advtrains.load()
end
--limit dtime: if trains move too far in one step, automation may cause stuck and wrongly braking trains
local dtime=dtime_mt
if dtime>0.2 then
atprint("Limiting dtime to 0.2!")
dtime=0.2
end
advtrains.mainloop_trainlogic(dtime)
if advtrains_itm_mainloop then
advtrains_itm_mainloop(dtime)
end
if atlatc then
atlatc.mainloop_stepcode(dtime)
atlatc.interrupt.mainloop(dtime)
end
--trigger a save when necessary
save_timer=save_timer-dtime
if save_timer<=0 then
local t=os.clock()
--save
advtrains.save()
save_timer=save_interval
atprintbm("saving", t)
end
return advtrains.pcall(function()
--call load once. see advtrains.load() comment
if not init_load then
advtrains.load()
end
--limit dtime: if trains move too far in one step, automation may cause stuck and wrongly braking trains
local dtime=dtime_mt
if dtime>0.2 then
atprint("Limiting dtime to 0.2!")
dtime=0.2
end
advtrains.mainloop_trainlogic(dtime)
if advtrains_itm_mainloop then
advtrains_itm_mainloop(dtime)
end
if atlatc then
atlatc.mainloop_stepcode(dtime)
atlatc.interrupt.mainloop(dtime)
end
--trigger a save when necessary
save_timer=save_timer-dtime
if save_timer<=0 then
local t=os.clock()
--save
advtrains.save()
save_timer=save_interval
atprintbm("saving", t)
end
end)
end)
--if something goes wrong in these functions, there is no help. no pcall here.
--## MAIN LOAD ROUTINE ##
-- Causes the loading of everything
-- first time called in main loop (after the init phase) because luaautomation has to initialize first.
@ -247,6 +274,8 @@ function advtrains.load()
advtrains_itm_init()
end
init_load=true
no_action=false
atprint("[load_all]Loaded advtrains save files")
end
--## MAIN SAVE ROUTINE ##
@ -261,5 +290,6 @@ function advtrains.save()
if atlatc then
atlatc.save()
end
atprint("[save_all]Saved advtrains save files")
end
minetest.register_on_shutdown(advtrains.save)

View File

@ -203,37 +203,41 @@ minetest.register_abm({
nodenames = {"group:save_in_nodedb"},
run_at_every_load = true,
action = function(pos, node)
local cid=ndbget(pos.x, pos.y, pos.z)
if cid then
--if in database, detect changes and apply.
local nodeid = ndb_nodeids[u14b(cid)]
local param2 = l2b(cid)
if not nodeid then
--something went wrong
atprint("nodedb: lbm nid not found", pos, "with nid", u14b(cid), "param2", param2, "cid is", cid)
ndb.update(pos, node)
else
if (nodeid~=node.name or param2~=node.param2) then
atprint("nodedb: lbm replaced", pos, "with nodeid", nodeid, "param2", param2, "cid is", cid)
minetest.swap_node(pos, {name=nodeid, param2 = param2})
local ndef=minetest.registered_nodes[nodeid]
if ndef and ndef.on_updated_from_nodedb then
ndef.on_updated_from_nodedb(pos, node)
return advtrains.pcall(function()
local cid=ndbget(pos.x, pos.y, pos.z)
if cid then
--if in database, detect changes and apply.
local nodeid = ndb_nodeids[u14b(cid)]
local param2 = l2b(cid)
if not nodeid then
--something went wrong
atprint("nodedb: lbm nid not found", pos, "with nid", u14b(cid), "param2", param2, "cid is", cid)
ndb.update(pos, node)
else
if (nodeid~=node.name or param2~=node.param2) then
atprint("nodedb: lbm replaced", pos, "with nodeid", nodeid, "param2", param2, "cid is", cid)
minetest.swap_node(pos, {name=nodeid, param2 = param2})
local ndef=minetest.registered_nodes[nodeid]
if ndef and ndef.on_updated_from_nodedb then
ndef.on_updated_from_nodedb(pos, node)
end
end
end
else
--if not in database, take it.
atprint("nodedb: ", pos, "not in database")
ndb.update(pos, node)
end
else
--if not in database, take it.
atprint("nodedb: ", pos, "not in database")
ndb.update(pos, node)
end
end)
end,
interval=10,
chance=1,
})
minetest.register_on_dignode(function(pos, oldnode, digger)
ndb.clear(pos)
return advtrains.pcall(function()
ndb.clear(pos)
end)
end)
function ndb.get_nodes()

View File

@ -187,26 +187,28 @@ function tp.register_track_placer(nnprefix, imgprefix, dispname)
wield_image = imgprefix.."_placer.png",
groups={},
on_place = function(itemstack, placer, pointed_thing)
local name = placer:get_player_name()
if not name then
return itemstack
end
if pointed_thing.type=="node" then
local pos=pointed_thing.above
local upos=vector.subtract(pointed_thing.above, {x=0, y=1, z=0})
if advtrains.is_protected(pos,name) then
minetest.record_protection_violation(pos, name)
return itemstack
return advtrains.pcall(function()
local name = placer:get_player_name()
if not name then
return itemstack
end
if minetest.registered_nodes[minetest.get_node(pos).name] and minetest.registered_nodes[minetest.get_node(pos).name].buildable_to
and minetest.registered_nodes[minetest.get_node(upos).name] and minetest.registered_nodes[minetest.get_node(upos).name].walkable then
tp.placetrack(pos, nnprefix, placer, itemstack, pointed_thing)
if not minetest.setting_getbool("creative_mode") then
itemstack:take_item()
if pointed_thing.type=="node" then
local pos=pointed_thing.above
local upos=vector.subtract(pointed_thing.above, {x=0, y=1, z=0})
if advtrains.is_protected(pos,name) then
minetest.record_protection_violation(pos, name)
return itemstack
end
if minetest.registered_nodes[minetest.get_node(pos).name] and minetest.registered_nodes[minetest.get_node(pos).name].buildable_to
and minetest.registered_nodes[minetest.get_node(upos).name] and minetest.registered_nodes[minetest.get_node(upos).name].walkable then
tp.placetrack(pos, nnprefix, placer, itemstack, pointed_thing)
if not minetest.setting_getbool("creative_mode") then
itemstack:take_item()
end
end
end
end
return itemstack
return itemstack
end)
end,
})
end
@ -220,78 +222,82 @@ minetest.register_craftitem("advtrains:trackworker",{
wield_image = "advtrains_trackworker.png",
stack_max = 1,
on_place = function(itemstack, placer, pointed_thing)
local name = placer:get_player_name()
if not name then
return
end
if pointed_thing.type=="node" then
local pos=pointed_thing.under
if advtrains.is_protected(pos, name) then
minetest.record_protection_violation(pos, name)
return advtrains.pcall(function()
local name = placer:get_player_name()
if not name then
return
end
local node=minetest.get_node(pos)
if pointed_thing.type=="node" then
local pos=pointed_thing.under
if advtrains.is_protected(pos, name) then
minetest.record_protection_violation(pos, name)
return
end
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.get_train_at_pos(pos) then return end
--if not advtrains.is_track_and_drives_on(minetest.get_node(pos).name, advtrains.all_tracktypes) then return end
if advtrains.get_train_at_pos(pos) then return end
local nnprefix, suffix, rotation=string.match(node.name, "^(.+)_([^_]+)(_[^_]+)$")
--atprint(node.name.."\npattern recognizes:"..nodeprefix.." / "..railtype.." / "..rotation)
if not tp.tracks[nnprefix] or not tp.tracks[nnprefix].twrotate[suffix] then
nnprefix, suffix=string.match(node.name, "^(.+)_([^_]+)$")
rotation = ""
local nnprefix, suffix, rotation=string.match(node.name, "^(.+)_([^_]+)(_[^_]+)$")
--atprint(node.name.."\npattern recognizes:"..nodeprefix.." / "..railtype.." / "..rotation)
if not tp.tracks[nnprefix] or not tp.tracks[nnprefix].twrotate[suffix] then
minetest.chat_send_player(placer:get_player_name(), attrans("This node can't be rotated using the trackworker!"))
return
end
end
local modext=tp.tracks[nnprefix].twrotate[suffix]
if rotation==modext[#modext] then --increase param2
advtrains.ndb.swap_node(pos, {name=nnprefix.."_"..suffix..modext[1], param2=(node.param2+1)%4})
return
else
local modpos
for k,v in pairs(modext) do if v==rotation then modpos=k end end
if not modpos then
nnprefix, suffix=string.match(node.name, "^(.+)_([^_]+)$")
rotation = ""
if not tp.tracks[nnprefix] or not tp.tracks[nnprefix].twrotate[suffix] then
minetest.chat_send_player(placer:get_player_name(), attrans("This node can't be rotated using the trackworker!"))
return
return
end
end
local modext=tp.tracks[nnprefix].twrotate[suffix]
if rotation==modext[#modext] then --increase param2
advtrains.ndb.swap_node(pos, {name=nnprefix.."_"..suffix..modext[1], param2=(node.param2+1)%4})
return
else
local modpos
for k,v in pairs(modext) do if v==rotation then modpos=k end end
if not modpos then
minetest.chat_send_player(placer:get_player_name(), attrans("This node can't be rotated using the trackworker!"))
return
end
advtrains.ndb.swap_node(pos, {name=nnprefix.."_"..suffix..modext[modpos+1], param2=node.param2})
end
advtrains.ndb.swap_node(pos, {name=nnprefix.."_"..suffix..modext[modpos+1], param2=node.param2})
end
end
end)
end,
on_use=function(itemstack, user, pointed_thing)
local name = user:get_player_name()
if not name then
return
end
if pointed_thing.type=="node" then
local pos=pointed_thing.under
local node=minetest.get_node(pos)
if advtrains.is_protected(pos, name) then
minetest.record_protection_violation(pos, name)
return
return advtrains.pcall(function()
local name = user:get_player_name()
if not name then
return
end
--if not advtrains.is_track_and_drives_on(minetest.get_node(pos).name, advtrains.all_tracktypes) then return end
if advtrains.get_train_at_pos(pos) then return end
local nnprefix, suffix, rotation=string.match(node.name, "^(.+)_([^_]+)(_[^_]+)$")
--atprint(node.name.."\npattern recognizes:"..nodeprefix.." / "..railtype.." / "..rotation)
if not tp.tracks[nnprefix] or not tp.tracks[nnprefix].twcycle[suffix] then
nnprefix, suffix=string.match(node.name, "^(.+)_([^_]+)$")
rotation = ""
if not tp.tracks[nnprefix] or not tp.tracks[nnprefix].twcycle[suffix] then
minetest.chat_send_player(user:get_player_name(), attrans("This node can't be changed using the trackworker!"))
return
end
end
local nextsuffix=tp.tracks[nnprefix].twcycle[suffix]
advtrains.ndb.swap_node(pos, {name=nnprefix.."_"..nextsuffix..rotation, param2=node.param2})
else
atprint(name, dump(tp.tracks))
end
if pointed_thing.type=="node" then
local pos=pointed_thing.under
local node=minetest.get_node(pos)
if advtrains.is_protected(pos, name) then
minetest.record_protection_violation(pos, name)
return
end
--if not advtrains.is_track_and_drives_on(minetest.get_node(pos).name, advtrains.all_tracktypes) then return end
if advtrains.get_train_at_pos(pos) then return end
local nnprefix, suffix, rotation=string.match(node.name, "^(.+)_([^_]+)(_[^_]+)$")
--atprint(node.name.."\npattern recognizes:"..nodeprefix.." / "..railtype.." / "..rotation)
if not tp.tracks[nnprefix] or not tp.tracks[nnprefix].twcycle[suffix] then
nnprefix, suffix=string.match(node.name, "^(.+)_([^_]+)$")
rotation = ""
if not tp.tracks[nnprefix] or not tp.tracks[nnprefix].twcycle[suffix] then
minetest.chat_send_player(user:get_player_name(), attrans("This node can't be changed using the trackworker!"))
return
end
end
local nextsuffix=tp.tracks[nnprefix].twcycle[suffix]
advtrains.ndb.swap_node(pos, {name=nnprefix.."_"..nextsuffix..rotation, param2=node.param2})
else
atprint(name, dump(tp.tracks))
end
end)
end,
})

View File

@ -37,8 +37,7 @@ advtrains.train_roll_force=0.5--per second, not divided by number of wagons, acc
advtrains.train_emerg_force=10--for emergency brakes(when going off track)
advtrains.mainloop_trainlogic(function(dtime)
advtrains.mainloop_trainlogic=function(dtime)
--build a table of all players indexed by pts. used by damage and door system.
advtrains.playersbypts={}
for _, player in pairs(minetest.get_connected_players()) do
@ -67,39 +66,43 @@ advtrains.mainloop_trainlogic(function(dtime)
end
minetest.register_on_joinplayer(function(player)
local pname=player:get_player_name()
local id=advtrains.player_to_train_mapping[pname]
if id then
local train=advtrains.trains[id]
if not train then advtrains.player_to_train_mapping[pname]=nil return end
--set the player to the train position.
--minetest will emerge the area and load the objects, which then will call reattach_all().
--because player is in mapping, it will not be subject to dying.
player:setpos(train.last_pos_prev)
--independent of this, cause all wagons of the train which are loaded to reattach their players
--needed because already loaded wagons won't call reattach_all()
for _,wagon in pairs(minetest.luaentities) do
if wagon.is_wagon and wagon.initialized and wagon.train_id==id then
wagon:reattach_all()
return advtrains.pcall(function()
local pname=player:get_player_name()
local id=advtrains.player_to_train_mapping[pname]
if id then
local train=advtrains.trains[id]
if not train then advtrains.player_to_train_mapping[pname]=nil return end
--set the player to the train position.
--minetest will emerge the area and load the objects, which then will call reattach_all().
--because player is in mapping, it will not be subject to dying.
player:setpos(train.last_pos_prev)
--independent of this, cause all wagons of the train which are loaded to reattach their players
--needed because already loaded wagons won't call reattach_all()
for _,wagon in pairs(minetest.luaentities) do
if wagon.is_wagon and wagon.initialized and wagon.train_id==id then
wagon:reattach_all()
end
end
end
end
end)
end)
minetest.register_on_dieplayer(function(player)
local pname=player:get_player_name()
local id=advtrains.player_to_train_mapping[pname]
if id then
local train=advtrains.trains[id]
if not train then advtrains.player_to_train_mapping[pname]=nil return end
for _,wagon in pairs(minetest.luaentities) do
if wagon.is_wagon and wagon.initialized and wagon.train_id==id then
--when player dies, detach him from the train
--call get_off_plr on every wagon since we don't know which one he's on.
wagon:get_off_plr(pname)
return advtrains.pcall(function()
local pname=player:get_player_name()
local id=advtrains.player_to_train_mapping[pname]
if id then
local train=advtrains.trains[id]
if not train then advtrains.player_to_train_mapping[pname]=nil return end
for _,wagon in pairs(minetest.luaentities) do
if wagon.is_wagon and wagon.initialized and wagon.train_id==id then
--when player dies, detach him from the train
--call get_off_plr on every wagon since we don't know which one he's on.
wagon:get_off_plr(pname)
end
end
end
end
end)
end)
--[[
train step structure:
@ -797,6 +800,10 @@ end
function advtrains.do_connect_trains(first_id, second_id, player)
local first, second=advtrains.trains[first_id], advtrains.trains[second_id]
if not first or not second or not first.index or not second.index or not first.end_index or not second.end_index then
return false
end
if first.couple_lock_back or second.couple_lock_front then
-- trains are ordered correctly!
if player then
@ -817,6 +824,7 @@ function advtrains.do_connect_trains(first_id, second_id, player)
advtrains.update_trainpart_properties(first_id)
advtrains.trains[first_id].velocity=new_velocity
advtrains.trains[first_id].tarvelocity=0
return true
end
function advtrains.invert_train(train_id)

View File

@ -50,19 +50,21 @@ function wagon:on_activate(sd_uid, dtime_s)
end
function wagon:get_staticdata()
if not self:ensure_init() then return end
atprint("[wagon "..((self.unique_id and self.unique_id~="" and self.unique_id) or "no-id").."]: saving to wagon_save")
--serialize inventory, if it has one
if self.has_inventory then
local inv=minetest.get_inventory({type="detached", name="advtrains_wgn_"..self.unique_id})
self.ser_inv=advtrains.serialize_inventory(inv)
end
--save to table before being unloaded
advtrains.wagon_save[self.unique_id]=advtrains.merge_tables(self)
advtrains.wagon_save[self.unique_id].entity_name=self.name
advtrains.wagon_save[self.unique_id].name=nil
advtrains.wagon_save[self.unique_id].object=nil
return self.unique_id
return advtrains.pcall(function()
if not self:ensure_init() then return end
atprint("[wagon "..((self.unique_id and self.unique_id~="" and self.unique_id) or "no-id").."]: saving to wagon_save")
--serialize inventory, if it has one
if self.has_inventory then
local inv=minetest.get_inventory({type="detached", name="advtrains_wgn_"..self.unique_id})
self.ser_inv=advtrains.serialize_inventory(inv)
end
--save to table before being unloaded
advtrains.wagon_save[self.unique_id]=advtrains.merge_tables(self)
advtrains.wagon_save[self.unique_id].entity_name=self.name
advtrains.wagon_save[self.unique_id].name=nil
advtrains.wagon_save[self.unique_id].object=nil
return self.unique_id
end)
end
--returns: uid of wagon
function wagon:init_new_instance(train_id, properties)
@ -147,36 +149,38 @@ end
-- Remove the wagon
function wagon:on_punch(puncher, time_from_last_punch, tool_capabilities, direction)
if not self:ensure_init() then return end
if not puncher or not puncher:is_player() then
return
end
if self.owner and puncher:get_player_name()~=self.owner and (not minetest.check_player_privs(puncher, {train_remove = true })) then
minetest.chat_send_player(puncher:get_player_name(), attrans("This wagon is owned by @1, you can't destroy it.", self.owner));
return
end
if minetest.setting_getbool("creative_mode") then
if not self:destroy() then return end
local inv = puncher:get_inventory()
if not inv:contains_item("main", self.name) then
inv:add_item("main", self.name)
end
else
local pc=puncher:get_player_control()
if not pc.sneak then
minetest.chat_send_player(puncher:get_player_name(), attrans("Warning: If you destroy this wagon, you only get some steel back! If you are sure, hold Sneak and left-click the wagon."))
return advtrains.pcall(function()
if not self:ensure_init() then return end
if not puncher or not puncher:is_player() then
return
end
if not self:destroy() then return end
local inv = puncher:get_inventory()
for _,item in ipairs(self.drops or {self.name}) do
inv:add_item("main", item)
if self.owner and puncher:get_player_name()~=self.owner and (not minetest.check_player_privs(puncher, {train_remove = true })) then
minetest.chat_send_player(puncher:get_player_name(), attrans("This wagon is owned by @1, you can't destroy it.", self.owner));
return
end
end
if minetest.setting_getbool("creative_mode") then
if not self:destroy() then return end
local inv = puncher:get_inventory()
if not inv:contains_item("main", self.name) then
inv:add_item("main", self.name)
end
else
local pc=puncher:get_player_control()
if not pc.sneak then
minetest.chat_send_player(puncher:get_player_name(), attrans("Warning: If you destroy this wagon, you only get some steel back! If you are sure, hold Sneak and left-click the wagon."))
return
end
if not self:destroy() then return end
local inv = puncher:get_inventory()
for _,item in ipairs(self.drops or {self.name}) do
inv:add_item("main", item)
end
end
end)
end
function wagon:destroy()
--some rules:
@ -211,260 +215,262 @@ end
function wagon:on_step(dtime)
if not self:ensure_init() then return end
local t=os.clock()
local pos = self.object:getpos()
if not pos then
atprint("["..self.unique_id.."][fatal] missing position (object:getpos() returned nil)")
return
end
self.entity_name=self.name
--is my train still here
if not self.train_id or not self:train() then
atprint("[wagon "..self.unique_id.."] missing train_id, destroying")
self.object:remove()
return
end
if not self.seatp then
self.seatp={}
end
if not self.seatpc then
self.seatpc={}
end
--Legacy: remove infotext since it does not work this way anyways
self.infotext=nil
--custom on_step function
if self.custom_on_step then
self:custom_on_step(self, dtime)
end
--driver control
for seatno, seat in ipairs(self.seats) do
local driver=self.seatp[seatno] and minetest.get_player_by_name(self.seatp[seatno])
local has_driverstand=seat.driving_ctrl_access and self.seatp[seatno] and minetest.check_player_privs(self.seatp[seatno], {train_operator=true})
if has_driverstand and driver then
advtrains.update_driver_hud(driver:get_player_name(), self:train(), self.wagon_flipped)
elseif driver then
--only show the inside text
local inside=self:train().text_inside or ""
advtrains.set_trainhud(driver:get_player_name(), inside)
return advtrains.pcall(function()
if not self:ensure_init() then return end
local t=os.clock()
local pos = self.object:getpos()
if not pos then
atprint("["..self.unique_id.."][fatal] missing position (object:getpos() returned nil)")
return
end
if driver and driver:get_player_control_bits()~=self.seatpc[seatno] then
local pc=driver:get_player_control()
self.seatpc[seatno]=driver:get_player_control_bits()
if has_driverstand then
--regular driver stand controls
advtrains.on_control_change(pc, self:train(), self.wagon_flipped)
else
-- If on a passenger seat and doors are open, get off when W or D pressed.
local pass = self.seatp[seatno] and minetest.get_player_by_name(self.seatp[seatno])
if pass and self:train().door_open~=0 then
local pc=pass:get_player_control()
if pc.up or pc.down then
self:get_off(seatno)
end
end
end
if pc.aux1 and pc.sneak then
self:get_off(seatno)
end
self.entity_name=self.name
--is my train still here
if not self.train_id or not self:train() then
atprint("[wagon "..self.unique_id.."] missing train_id, destroying")
self.object:remove()
return
end
end
--check infotext
local outside=self:train().text_outside or ""
if self.object:get_properties().infotext~=outside then
self.object:set_properties({infotext=outside})
end
local gp=self:train()
local fct=self.wagon_flipped and -1 or 1
--door animation
if self.doors then
if (self.door_anim_timer or 0)<=0 then
local dstate = (gp.door_open or 0) * fct
if dstate ~= self.door_state then
local at
--meaning of the train.door_open field:
-- -1: left doors (rel. to train orientation)
-- 0: closed
-- 1: right doors
--this code produces the following behavior:
-- if changed from 0 to +-1, play open anim. if changed from +-1 to 0, play close.
-- if changed from +-1 to -+1, first close and set 0, then it will detect state change again and run open.
if self.door_state == 0 then
at=self.doors.open[dstate]
self.object:set_animation(at.frames, at.speed or 15, at.blend or 0, false)
self.door_state = dstate
if not self.seatp then
self.seatp={}
end
if not self.seatpc then
self.seatpc={}
end
--Legacy: remove infotext since it does not work this way anyways
self.infotext=nil
--custom on_step function
if self.custom_on_step then
self:custom_on_step(self, dtime)
end
--driver control
for seatno, seat in ipairs(self.seats) do
local driver=self.seatp[seatno] and minetest.get_player_by_name(self.seatp[seatno])
local has_driverstand=seat.driving_ctrl_access and self.seatp[seatno] and minetest.check_player_privs(self.seatp[seatno], {train_operator=true})
if has_driverstand and driver then
advtrains.update_driver_hud(driver:get_player_name(), self:train(), self.wagon_flipped)
elseif driver then
--only show the inside text
local inside=self:train().text_inside or ""
advtrains.set_trainhud(driver:get_player_name(), inside)
end
if driver and driver:get_player_control_bits()~=self.seatpc[seatno] then
local pc=driver:get_player_control()
self.seatpc[seatno]=driver:get_player_control_bits()
if has_driverstand then
--regular driver stand controls
advtrains.on_control_change(pc, self:train(), self.wagon_flipped)
else
at=self.doors.close[self.door_state or 1]--in case it has not been set yet
self.object:set_animation(at.frames, at.speed or 15, at.blend or 0, false)
self.door_state = 0
-- If on a passenger seat and doors are open, get off when W or D pressed.
local pass = self.seatp[seatno] and minetest.get_player_by_name(self.seatp[seatno])
if pass and self:train().door_open~=0 then
local pc=pass:get_player_control()
if pc.up or pc.down then
self:get_off(seatno)
end
end
end
self.door_anim_timer = at.time
end
else
self.door_anim_timer = (self.door_anim_timer or 0) - dtime
end
end
--DisCouple
if self.pos_in_trainparts and self.pos_in_trainparts>1 then
if gp.velocity==0 and not self.lock_couples then
if not self.discouple or not self.discouple.object:getyaw() then
local object=minetest.add_entity(pos, "advtrains:discouple")
if object then
local le=object:get_luaentity()
le.wagon=self
--box is hidden when attached, so unuseful.
--object:set_attach(self.object, "", {x=0, y=0, z=self.wagon_span*10}, {x=0, y=0, z=0})
self.discouple=le
else
atprint("Couldn't spawn DisCouple")
if pc.aux1 and pc.sneak then
self:get_off(seatno)
end
end
else
if self.discouple and self.discouple.object:getyaw() then
self.discouple.object:remove()
end
end
end
--for path to be available. if not, skip step
if not gp.path then
self.object:setvelocity({x=0, y=0, z=0})
return
end
if not self.pos_in_train then
--why ever. but better continue next step...
advtrains.update_trainpart_properties(self.train_id)
return
end
local index=advtrains.get_real_path_index(self:train(), self.pos_in_train)
--atprint("trainindex "..gp.index.." wagonindex "..index)
--automatic get_on
--needs to know index and path
if self.door_entry and gp.door_open and gp.door_open~=0 and gp.velocity==0 then
--using the mapping created by the trainlogic globalstep
for i, ino in ipairs(self.door_entry) do
--fct is the flipstate flag from door animation above
local aci = index + ino*fct
local ix1=gp.path[math.floor(aci)]
local ix2=gp.path[math.floor(aci+1)]
-- the two wanted positions are ix1 and ix2 + (2nd-1st rotated by 90deg)
-- (x z) rotated by 90deg is (-z x) (http://stackoverflow.com/a/4780141)
local add = { x = (ix2.z-ix1.z)*gp.door_open, y = 0, z = (ix1.x-ix2.x)*gp.door_open }
local pts1=vector.round(vector.add(ix1, add))
local pts2=vector.round(vector.add(ix2, add))
if minetest.get_item_group(minetest.get_node(pts1).name, "platform")>0 then
local ckpts={
pts1,
pts2,
vector.add(pts1, {x=0, y=1, z=0}),
vector.add(pts2, {x=0, y=1, z=0}),
}
for _,ckpos in ipairs(ckpts) do
local cpp=minetest.pos_to_string(ckpos)
if advtrains.playersbypts[cpp] then
self:on_rightclick(advtrains.playersbypts[cpp])
--check infotext
local outside=self:train().text_outside or ""
if self.object:get_properties().infotext~=outside then
self.object:set_properties({infotext=outside})
end
local gp=self:train()
local fct=self.wagon_flipped and -1 or 1
--door animation
if self.doors then
if (self.door_anim_timer or 0)<=0 then
local dstate = (gp.door_open or 0) * fct
if dstate ~= self.door_state then
local at
--meaning of the train.door_open field:
-- -1: left doors (rel. to train orientation)
-- 0: closed
-- 1: right doors
--this code produces the following behavior:
-- if changed from 0 to +-1, play open anim. if changed from +-1 to 0, play close.
-- if changed from +-1 to -+1, first close and set 0, then it will detect state change again and run open.
if self.door_state == 0 then
at=self.doors.open[dstate]
self.object:set_animation(at.frames, at.speed or 15, at.blend or 0, false)
self.door_state = dstate
else
at=self.doors.close[self.door_state or 1]--in case it has not been set yet
self.object:set_animation(at.frames, at.speed or 15, at.blend or 0, false)
self.door_state = 0
end
self.door_anim_timer = at.time
end
end
end
end
--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
--atprint(" 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 (advtrains.train_collides(node)) then
collides=true
end
end
end
end
if collides then
if self.collision_count and self.collision_count>10 then
--enable collision mercy to get trains stuck in walls out of walls
--actually do nothing except limiting the velocity to 1
gp.velocity=math.min(gp.velocity, 1)
gp.tarvelocity=math.min(gp.tarvelocity, 1)
else
gp.recently_collided_with_env=true
gp.velocity=2*gp.velocity
gp.movedir=-gp.movedir
gp.tarvelocity=0
self.collision_count=(self.collision_count or 0)+1
self.door_anim_timer = (self.door_anim_timer or 0) - dtime
end
end
--DisCouple
if self.pos_in_trainparts and self.pos_in_trainparts>1 then
if gp.velocity==0 and not self.lock_couples then
if not self.discouple or not self.discouple.object:getyaw() then
local object=minetest.add_entity(pos, "advtrains:discouple")
if object then
local le=object:get_luaentity()
le.wagon=self
--box is hidden when attached, so unuseful.
--object:set_attach(self.object, "", {x=0, y=0, z=self.wagon_span*10}, {x=0, y=0, z=0})
self.discouple=le
else
atprint("Couldn't spawn DisCouple")
end
end
else
if self.discouple and self.discouple.object:getyaw() then
self.discouple.object:remove()
end
end
end
--for path to be available. if not, skip step
if not gp.path then
self.object:setvelocity({x=0, y=0, z=0})
return
end
if not self.pos_in_train then
--why ever. but better continue next step...
advtrains.update_trainpart_properties(self.train_id)
return
end
local index=advtrains.get_real_path_index(self:train(), self.pos_in_train)
--atprint("trainindex "..gp.index.." wagonindex "..index)
--automatic get_on
--needs to know index and path
if self.door_entry and gp.door_open and gp.door_open~=0 and gp.velocity==0 then
--using the mapping created by the trainlogic globalstep
for i, ino in ipairs(self.door_entry) do
--fct is the flipstate flag from door animation above
local aci = index + ino*fct
local ix1=gp.path[math.floor(aci)]
local ix2=gp.path[math.floor(aci+1)]
-- the two wanted positions are ix1 and ix2 + (2nd-1st rotated by 90deg)
-- (x z) rotated by 90deg is (-z x) (http://stackoverflow.com/a/4780141)
local add = { x = (ix2.z-ix1.z)*gp.door_open, y = 0, z = (ix1.x-ix2.x)*gp.door_open }
local pts1=vector.round(vector.add(ix1, add))
local pts2=vector.round(vector.add(ix2, add))
if minetest.get_item_group(minetest.get_node(pts1).name, "platform")>0 then
local ckpts={
pts1,
pts2,
vector.add(pts1, {x=0, y=1, z=0}),
vector.add(pts2, {x=0, y=1, z=0}),
}
for _,ckpos in ipairs(ckpts) do
local cpp=minetest.pos_to_string(ckpos)
if advtrains.playersbypts[cpp] then
self:on_rightclick(advtrains.playersbypts[cpp])
end
end
end
end
end
--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
--atprint(" 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 (advtrains.train_collides(node)) then
collides=true
end
end
end
end
if collides then
if self.collision_count and self.collision_count>10 then
--enable collision mercy to get trains stuck in walls out of walls
--actually do nothing except limiting the velocity to 1
gp.velocity=math.min(gp.velocity, 1)
gp.tarvelocity=math.min(gp.tarvelocity, 1)
else
gp.recently_collided_with_env=true
gp.velocity=2*gp.velocity
gp.movedir=-gp.movedir
gp.tarvelocity=0
self.collision_count=(self.collision_count or 0)+1
end
else
self.collision_count=nil
end
end
--FIX: use index of the wagon, not of the train.
local velocity=(gp.velocity*gp.movedir)/(gp.path_dist[math.floor(index)] or 1)
local acceleration=(gp.last_accel or 0)/(gp.path_dist[math.floor(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}
local accelerationvec={x=(first_pos.x-second_pos.x)*acceleration*-1, z=(first_pos.z-second_pos.z)*acceleration*-1, y=(first_pos.y-second_pos.y)*acceleration*-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
self.collision_count=nil
yaw=math.atan2((first_pos.x-second_pos.x), (second_pos.z-first_pos.z))
end
end
--FIX: use index of the wagon, not of the train.
local velocity=(gp.velocity*gp.movedir)/(gp.path_dist[math.floor(index)] or 1)
local acceleration=(gp.last_accel or 0)/(gp.path_dist[math.floor(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}
local accelerationvec={x=(first_pos.x-second_pos.x)*acceleration*-1, z=(first_pos.z-second_pos.z)*acceleration*-1, y=(first_pos.y-second_pos.y)*acceleration*-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 not self.old_velocity_vector
or not vector.equals(velocityvec, self.old_velocity_vector)
or not self.old_acceleration_vector
or not vector.equals(accelerationvec, self.old_acceleration_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:setacceleration(accelerationvec)
self.object:setyaw(yaw)
self.updatepct_timer=2
if self.update_animation then
self:update_animation(gp.velocity)
if self.wagon_flipped then
yaw=yaw+math.pi
end
end
self.old_velocity_vector=velocityvec
self.old_acceleration_vector=accelerationvec
self.old_yaw=yaw
atprintbm("wagon step", t)
self.updatepct_timer=(self.updatepct_timer or 0)-dtime
if not self.old_velocity_vector
or not vector.equals(velocityvec, self.old_velocity_vector)
or not self.old_acceleration_vector
or not vector.equals(accelerationvec, self.old_acceleration_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:setacceleration(accelerationvec)
self.object:setyaw(yaw)
self.updatepct_timer=2
if self.update_animation then
self:update_animation(gp.velocity)
end
end
self.old_velocity_vector=velocityvec
self.old_acceleration_vector=accelerationvec
self.old_yaw=yaw
atprintbm("wagon step", t)
end)
end
function advtrains.get_real_path_index(train, pit)
@ -485,85 +491,87 @@ function advtrains.get_real_path_index(train, pit)
end
function wagon:on_rightclick(clicker)
if not self:ensure_init() then return end
if not clicker or not clicker:is_player() then
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)
atprint(dump(self))
return
end
local pname=clicker:get_player_name()
local no=self:get_seatno(pname)
if no then
if self.seat_groups then
local poss={}
local sgr=self.seats[no].group
for _,access in ipairs(self.seat_groups[sgr].access_to) do
if self:check_seat_group_access(pname, access) then
poss[#poss+1]={name=self.seat_groups[access].name, key="sgr_"..access}
return advtrains.pcall(function()
if not self:ensure_init() then return end
if not clicker or not clicker:is_player() then
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)
atprint(dump(self))
return
end
local pname=clicker:get_player_name()
local no=self:get_seatno(pname)
if no then
if self.seat_groups then
local poss={}
local sgr=self.seats[no].group
for _,access in ipairs(self.seat_groups[sgr].access_to) do
if self:check_seat_group_access(pname, access) then
poss[#poss+1]={name=self.seat_groups[access].name, key="sgr_"..access}
end
end
end
if self.has_inventory and self.get_inventory_formspec then
poss[#poss+1]={name=attrans("Show Inventory"), key="inv"}
end
if self.owner==pname then
poss[#poss+1]={name=attrans("Wagon properties"), key="prop"}
end
if not self.seat_groups[sgr].require_doors_open or self:train().door_open~=0 then
poss[#poss+1]={name=attrans("Get off"), key="off"}
else
if clicker:get_player_control().sneak then
poss[#poss+1]={name=attrans("Get off (forced)"), key="off"}
if self.has_inventory and self.get_inventory_formspec then
poss[#poss+1]={name=attrans("Show Inventory"), key="inv"}
end
if self.owner==pname then
poss[#poss+1]={name=attrans("Wagon properties"), key="prop"}
end
if not self.seat_groups[sgr].require_doors_open or self:train().door_open~=0 then
poss[#poss+1]={name=attrans("Get off"), key="off"}
else
poss[#poss+1]={name=attrans("(Doors closed)"), key="dcwarn"}
if clicker:get_player_control().sneak then
poss[#poss+1]={name=attrans("Get off (forced)"), key="off"}
else
poss[#poss+1]={name=attrans("(Doors closed)"), key="dcwarn"}
end
end
if #poss==0 then
--can't do anything.
elseif #poss==1 then
self:seating_from_key_helper(pname, {[poss[1].key]=true}, no)
else
local form = "size[5,"..1+(#poss).."]"
for pos,ent in ipairs(poss) do
form = form .. "button_exit[0.5,"..(pos-0.5)..";4,1;"..ent.key..";"..ent.name.."]"
end
minetest.show_formspec(pname, "advtrains_seating_"..self.unique_id, form)
end
end
if #poss==0 then
--can't do anything.
elseif #poss==1 then
self:seating_from_key_helper(pname, {[poss[1].key]=true}, no)
else
local form = "size[5,"..1+(#poss).."]"
for pos,ent in ipairs(poss) do
form = form .. "button_exit[0.5,"..(pos-0.5)..";4,1;"..ent.key..";"..ent.name.."]"
end
minetest.show_formspec(pname, "advtrains_seating_"..self.unique_id, form)
self:get_off(no)
end
else
self:get_off(no)
end
else
--do not attach if already on a train
if advtrains.player_to_train_mapping[pname] then return end
if self.seat_groups then
if #self.seats==0 then
if self.has_inventory and self.get_inventory_formspec then
minetest.show_formspec(pname, "advtrains_inv_"..self.unique_id, self:get_inventory_formspec(pname))
--do not attach if already on a train
if advtrains.player_to_train_mapping[pname] then return end
if self.seat_groups then
if #self.seats==0 then
if self.has_inventory and self.get_inventory_formspec then
minetest.show_formspec(pname, "advtrains_inv_"..self.unique_id, self:get_inventory_formspec(pname))
end
return
end
return
end
local doors_open = self:train().door_open~=0 or clicker:get_player_control().sneak
for _,sgr in ipairs(self.assign_to_seat_group) do
if self:check_seat_group_access(pname, sgr) then
for seatid, seatdef in ipairs(self.seats) do
if seatdef.group==sgr and not self.seatp[seatid] and (not self.seat_groups[sgr].require_doors_open or doors_open) then
self:get_on(clicker, seatid)
return
local doors_open = self:train().door_open~=0 or clicker:get_player_control().sneak
for _,sgr in ipairs(self.assign_to_seat_group) do
if self:check_seat_group_access(pname, sgr) then
for seatid, seatdef in ipairs(self.seats) do
if seatdef.group==sgr and not self.seatp[seatid] and (not self.seat_groups[sgr].require_doors_open or doors_open) then
self:get_on(clicker, seatid)
return
end
end
end
end
minetest.chat_send_player(pname, attrans("Can't get on: wagon full or doors closed!"))
minetest.chat_send_player(pname, attrans("Use Sneak+rightclick to bypass closed doors!"))
else
self:show_get_on_form(pname)
end
minetest.chat_send_player(pname, attrans("Can't get on: wagon full or doors closed!"))
minetest.chat_send_player(pname, attrans("Use Sneak+rightclick to bypass closed doors!"))
else
self:show_get_on_form(pname)
end
end
end)
end
function wagon:get_on(clicker, seatno)
@ -708,77 +716,79 @@ function wagon:show_wagon_properties(pname)
minetest.show_formspec(pname, "advtrains_prop_"..self.unique_id, form)
end
minetest.register_on_player_receive_fields(function(player, formname, fields)
local uid=string.match(formname, "^advtrains_geton_(.+)$")
if uid then
for _,wagon in pairs(minetest.luaentities) do
if wagon.is_wagon and wagon.initialized and wagon.unique_id==uid then
if fields.inv then
if wagon.has_inventory and wagon.get_inventory_formspec then
minetest.show_formspec(player:get_player_name(), "advtrains_inv_"..uid, wagon:get_inventory_formspec(player:get_player_name()))
end
elseif fields.seat then
local val=minetest.explode_textlist_event(fields.seat)
if val and val.type~="INV" and not wagon.seatp[player:get_player_name()] then
--get on
wagon:get_on(player, val.index)
--will work with the new close_formspec functionality. close exactly this formspec.
minetest.show_formspec(player:get_player_name(), formname, "")
return advtrains.pcall(function()
local uid=string.match(formname, "^advtrains_geton_(.+)$")
if uid then
for _,wagon in pairs(minetest.luaentities) do
if wagon.is_wagon and wagon.initialized and wagon.unique_id==uid then
if fields.inv then
if wagon.has_inventory and wagon.get_inventory_formspec then
minetest.show_formspec(player:get_player_name(), "advtrains_inv_"..uid, wagon:get_inventory_formspec(player:get_player_name()))
end
elseif fields.seat then
local val=minetest.explode_textlist_event(fields.seat)
if val and val.type~="INV" and not wagon.seatp[player:get_player_name()] then
--get on
wagon:get_on(player, val.index)
--will work with the new close_formspec functionality. close exactly this formspec.
minetest.show_formspec(player:get_player_name(), formname, "")
end
end
end
end
end
end
uid=string.match(formname, "^advtrains_seating_(.+)$")
if uid then
for _,wagon in pairs(minetest.luaentities) do
if wagon.is_wagon and wagon.initialized and wagon.unique_id==uid then
local pname=player:get_player_name()
local no=wagon:get_seatno(pname)
if no then
if wagon.seat_groups then
wagon:seating_from_key_helper(pname, fields, no)
uid=string.match(formname, "^advtrains_seating_(.+)$")
if uid then
for _,wagon in pairs(minetest.luaentities) do
if wagon.is_wagon and wagon.initialized and wagon.unique_id==uid then
local pname=player:get_player_name()
local no=wagon:get_seatno(pname)
if no then
if wagon.seat_groups then
wagon:seating_from_key_helper(pname, fields, no)
end
end
end
end
end
end
uid=string.match(formname, "^advtrains_prop_(.+)$")
if uid then
for _,wagon in pairs(minetest.luaentities) do
if wagon.is_wagon and wagon.initialized and wagon.unique_id==uid then
local pname=player:get_player_name()
if pname~=wagon.owner then
return true
end
if fields.save or not fields.quit then
for sgr,sgrdef in pairs(wagon.seat_groups) do
if fields["sgr_"..sgr] then
local fcont = fields["sgr_"..sgr]
wagon.seat_access[sgr] = fcont~="" and fcont or nil
uid=string.match(formname, "^advtrains_prop_(.+)$")
if uid then
for _,wagon in pairs(minetest.luaentities) do
if wagon.is_wagon and wagon.initialized and wagon.unique_id==uid then
local pname=player:get_player_name()
if pname~=wagon.owner then
return true
end
if fields.save or not fields.quit then
for sgr,sgrdef in pairs(wagon.seat_groups) do
if fields["sgr_"..sgr] then
local fcont = fields["sgr_"..sgr]
wagon.seat_access[sgr] = fcont~="" and fcont or nil
end
end
end
if fields.lock_couples then
wagon.lock_couples = fields.lock_couples == "true"
end
if fields.text_outside then
if fields.text_outside~="" then
wagon:train().text_outside=fields.text_outside
else
wagon:train().text_outside=nil
if fields.lock_couples then
wagon.lock_couples = fields.lock_couples == "true"
end
end
if fields.text_inside then
if fields.text_inside~="" then
wagon:train().text_inside=fields.text_inside
else
wagon:train().text_inside=nil
if fields.text_outside then
if fields.text_outside~="" then
wagon:train().text_outside=fields.text_outside
else
wagon:train().text_outside=nil
end
end
if fields.text_inside then
if fields.text_inside~="" then
wagon:train().text_inside=fields.text_inside
else
wagon:train().text_inside=nil
end
end
end
end
end
end
end
end)
end)
function wagon:seating_from_key_helper(pname, fields, no)
local sgr=self.seats[no].group
@ -841,41 +851,43 @@ function advtrains.register_wagon(sysname, prototype, desc, inv_img)
stack_max = 1,
on_place = function(itemstack, placer, pointed_thing)
if not pointed_thing.type == "node" then
return
end
return advtrains.pcall(function()
if not pointed_thing.type == "node" then
return
end
local node=minetest.get_node_or_nil(pointed_thing.under)
if not node then atprint("[advtrains]Ignore at placer position") return itemstack end
local nodename=node.name
if(not advtrains.is_track_and_drives_on(nodename, prototype.drives_on)) then
atprint("no track here, not placing.")
local node=minetest.get_node_or_nil(pointed_thing.under)
if not node then atprint("[advtrains]Ignore at placer position") return itemstack end
local nodename=node.name
if(not advtrains.is_track_and_drives_on(nodename, prototype.drives_on)) then
atprint("no track here, not placing.")
return itemstack
end
if not minetest.check_player_privs(placer, {train_place = true }) and minetest.is_protected(pointed_thing.under, placer:get_player_name()) then
minetest.record_protection_violation(pointed_thing.under, placer:get_player_name())
return
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))
local ob=minetest.add_entity(pointed_thing.under, "advtrains:"..sysname)
if not ob then
atprint("couldn't add_entity, aborting")
end
local le=ob:get_luaentity()
le.owner=placer:get_player_name()
local wagon_uid=le:init_new_instance(id, {})
advtrains.add_wagon_to_train(le, id)
if not minetest.setting_getbool("creative_mode") then
itemstack:take_item()
end
return itemstack
end
if not minetest.check_player_privs(placer, {train_place = true }) and minetest.is_protected(pointed_thing.under, placer:get_player_name()) then
minetest.record_protection_violation(pointed_thing.under, placer:get_player_name())
return
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))
local ob=minetest.add_entity(pointed_thing.under, "advtrains:"..sysname)
if not ob then
atprint("couldn't add_entity, aborting")
end
local le=ob:get_luaentity()
le.owner=placer:get_player_name()
local wagon_uid=le:init_new_instance(id, {})
advtrains.add_wagon_to_train(le, id)
if not minetest.setting_getbool("creative_mode") then
itemstack:take_item()
end
return itemstack
end)
end,
})
end

View File

@ -136,7 +136,7 @@ function advtrains_itm_mainloop(dtime)
end
timer=2
end
end)
end
minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname=="itrainmap" and fields.quit then
itm_pdata[player:get_player_name()]=nil

View File

@ -62,10 +62,10 @@ function atlatc.load()
end
file:close()
end
-- run init code of all environments
atlatc.run_initcode()
end
-- run init code of all environments
atlatc.run_initcode()
atlatc.save = function()
--versions:
@ -107,4 +107,4 @@ function atlatc.mainloop_stepcode(dtime)
timer=0
atlatc.run_stepcode()
end
end)
end

View File

@ -22,26 +22,24 @@ function iq.add(t, pos, evtdata)
end
function iq.mainloop(dtime)
if run then
timer=timer + math.min(dtime, 0.2)
for i=1,#queue do
local qe=queue[i]
if not qe then
table.remove(queue, i)
i=i-1
elseif timer>qe.t then
local pos, evtdata=queue[i].p, queue[i].e
local node=advtrains.ndb.get_node(pos)
local ndef=minetest.registered_nodes[node.name]
if ndef and ndef.luaautomation and ndef.luaautomation.fire_event then
ndef.luaautomation.fire_event(pos, evtdata)
end
table.remove(queue, i)
i=i-1
timer=timer + math.min(dtime, 0.2)
for i=1,#queue do
local qe=queue[i]
if not qe then
table.remove(queue, i)
i=i-1
elseif timer>qe.t then
local pos, evtdata=queue[i].p, queue[i].e
local node=advtrains.ndb.get_node(pos)
local ndef=minetest.registered_nodes[node.name]
if ndef and ndef.luaautomation and ndef.luaautomation.fire_event then
ndef.luaautomation.fire_event(pos, evtdata)
end
table.remove(queue, i)
i=i-1
end
end
end)
end