Add Automatic Train Control system
|
@ -103,7 +103,7 @@ minetest.register_node(nodename, {
|
|||
after_place_node=function(pos)
|
||||
advtrains.reset_trackdb_position(pos)
|
||||
end,
|
||||
^- the code in these 3 functions is required for advtrains to work.
|
||||
^- the code in these 3 default minetest API functions is required for advtrains to work, however you can add your own code
|
||||
|
||||
advtrains = {
|
||||
on_train_enter=function(pos, train_id) end
|
||||
|
|
|
@ -1,4 +1,294 @@
|
|||
--atc.lua
|
||||
--registers and controls the ATC system
|
||||
|
||||
--(simple)mesecon detector rails
|
||||
local atc={}
|
||||
-- ATC persistence table
|
||||
atc.controllers = {}
|
||||
--contents: {command="...", arrowconn=0-15 where arrow points}
|
||||
|
||||
advtrains.fpath_atc=minetest.get_worldpath().."/advtrains_atc"
|
||||
local file, err = io.open(advtrains.fpath_atc, "r")
|
||||
if not file then
|
||||
local er=err or "Unknown Error"
|
||||
print("[advtrains]Failed loading advtrains atc save file "..er)
|
||||
else
|
||||
local tbl = minetest.deserialize(file:read("*a"))
|
||||
if type(tbl) == "table" then
|
||||
atc.controllers=tbl.controllers
|
||||
end
|
||||
file:close()
|
||||
end
|
||||
function atc.save()
|
||||
--leave space for more save data.
|
||||
local datastr = minetest.serialize({controllers = atc.controllers})
|
||||
if not datastr then
|
||||
minetest.log("error", "[advtrains] Failed to serialize trackdb data!")
|
||||
return
|
||||
end
|
||||
local file, err = io.open(advtrains.fpath_atc, "w")
|
||||
if err then
|
||||
return err
|
||||
end
|
||||
file:write(datastr)
|
||||
file:close()
|
||||
end
|
||||
|
||||
--call from advtrains.detector subprogram
|
||||
|
||||
function atc.trigger_controller_train_enter(pos, train_id)
|
||||
atc.send_command(pos)
|
||||
end
|
||||
|
||||
--general
|
||||
|
||||
function atc.send_command(pos)
|
||||
local pts=minetest.pos_to_string(pos)
|
||||
if atc.controllers[pts] then
|
||||
print("Called send_command at "..pts)
|
||||
local train_id = advtrains.detector.on_node[pts]
|
||||
if train_id then
|
||||
if advtrains.trains[train_id] then
|
||||
print("send_command inside if: "..sid(train_id))
|
||||
atc.train_reset_command(train_id)
|
||||
local arrowconn=atc.controllers[pts].arrowconn
|
||||
local train=advtrains.trains[train_id]
|
||||
for index, ppos in pairs(train.path) do
|
||||
if vector.equals(advtrains.round_vector_floor_y(ppos), pos) then
|
||||
advtrains.trains[train_id].atc_arrow =
|
||||
vector.equals(
|
||||
advtrains.dirCoordSet(pos, arrowconn),
|
||||
advtrains.round_vector_floor_y(train.path[index+train.movedir])
|
||||
)
|
||||
advtrains.trains[train_id].atc_command=atc.controllers[pts].command
|
||||
print("Sending ATC Command: "..atc.controllers[pts].command)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function atc.train_reset_command(train_id)
|
||||
advtrains.trains[train_id].atc_command=nil
|
||||
advtrains.trains[train_id].atc_delay=0
|
||||
advtrains.trains[train_id].atc_brake_target=nil
|
||||
advtrains.trains[train_id].atc_wait_finish=nil
|
||||
advtrains.trains[train_id].atc_arrow=nil
|
||||
end
|
||||
|
||||
--nodes
|
||||
local idxtrans={static=1, mesecon=2, digiline=3}
|
||||
local apn_func=function(pos)
|
||||
advtrains.reset_trackdb_position(pos)
|
||||
local meta=minetest.get_meta(pos)
|
||||
if meta then
|
||||
meta:set_string("infotext", "ATC controller, unconfigured.")
|
||||
meta:set_string("formspec", atc.get_atc_controller_formspec(pos, meta))
|
||||
end
|
||||
end
|
||||
|
||||
advtrains.register_tracks("default", {
|
||||
nodename_prefix="advtrains:dtrack_atc",
|
||||
texture_prefix="advtrains_dtrack_atc",
|
||||
models_prefix="advtrains_dtrack_detector",
|
||||
models_suffix=".b3d",
|
||||
shared_texture="advtrains_dtrack_rail_atc.png",
|
||||
description="ATC controller",
|
||||
formats={},
|
||||
get_additional_definiton = function(def, preset, suffix, rotation)
|
||||
return {
|
||||
after_place_node=apn_func,
|
||||
on_place_rail=apn_func,
|
||||
after_dig_node=function(pos)
|
||||
advtrains.invalidate_all_paths()
|
||||
advtrains.reset_trackdb_position(pos)
|
||||
local pts=minetest.pos_to_string(pos)
|
||||
atc.controllers[pts]=nil
|
||||
end,
|
||||
on_receive_fields = function(pos, formname, fields, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
minetest.chat_send_player(player:get_player_name(), "This position is protected!")
|
||||
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])
|
||||
meta:set_string("infotext", "ATC controller, mode "..fields.mode.."\n"..( fields.mode=="digiline" and "Channel: "..meta:get_string("channel") or "Command: "..meta:get_string("command") ) )
|
||||
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)
|
||||
meta:set_string("infotext", "ATC controller, mode "..fields.mode.."\n"..( fields.mode=="digiline" and "Channel: "..meta:get_string("channel") or "Command: "..meta:get_string("command") ) )
|
||||
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}
|
||||
atc.send_command(pos)
|
||||
end
|
||||
end,
|
||||
}
|
||||
end
|
||||
}, advtrains.trackpresets.t_30deg_straightonly)
|
||||
|
||||
|
||||
function atc.get_atc_controller_formspec(pos, meta)
|
||||
local mode=tonumber(meta:get_string("mode")) or 1
|
||||
local command=meta:get_string("command")
|
||||
local command_on=meta:get_string("command_on")
|
||||
local channel=meta:get_string("channel")
|
||||
local formspec="size[8,6]"..
|
||||
"dropdown[0,0;3;mode;static,mesecon,digiline;"..mode.."]"
|
||||
if mode<3 then
|
||||
formspec=formspec.."field[0.5,1.5;7,1;command;Command;"..minetest.formspec_escape(command).."]"
|
||||
if tonumber(mode)==2 then
|
||||
formspec=formspec.."field[0.5,3;7,1;command_on;Command (on);"..minetest.formspec_escape(command_on).."]"
|
||||
end
|
||||
else
|
||||
formspec=formspec.."field[0.5,1.5;7,1;channel;Digiline channel;"..minetest.formspec_escape(channel).."]"
|
||||
end
|
||||
return formspec.."button_exit[0.5,4.5;7,1;save;Save]"
|
||||
end
|
||||
|
||||
--from trainlogic.lua train step
|
||||
local matchptn={
|
||||
["SM"]=function(id, train)
|
||||
train.tarvelocity=train.max_speed
|
||||
return 2
|
||||
end,
|
||||
["S([0-9]+)"]=function(id, train, match)
|
||||
train.tarvelocity=tonumber(match)
|
||||
return #match+1
|
||||
end,
|
||||
["B([0-9]+)"]=function(id, train, match)
|
||||
if train.velocity>tonumber(match) then
|
||||
train.atc_brake_target=tonumber(match)
|
||||
if train.tarvelocity>train.atc_brake_target then
|
||||
train.tarvelocity=train.atc_brake_target
|
||||
end
|
||||
end
|
||||
return #match+1
|
||||
end,
|
||||
["W"]=function(id, train)
|
||||
train.atc_wait_finish=true
|
||||
return 1
|
||||
end,
|
||||
["D([0-9]+)"]=function(id, train, match)
|
||||
train.atc_delay=tonumber(match)
|
||||
return #match+1
|
||||
end,
|
||||
["R"]=function(id, train)
|
||||
if train.velocity<=0 then
|
||||
train.movedir=train.movedir*-1
|
||||
train.atc_arrow = not train.atc_arrow
|
||||
else
|
||||
print("ATC Reverse command warning: didn't reverse train!")
|
||||
end
|
||||
return 1
|
||||
end,
|
||||
}
|
||||
|
||||
function atc.execute_atc_command(id, train)
|
||||
--strip whitespaces
|
||||
local command=string.match(train.atc_command, "^%s*(.*)$")
|
||||
|
||||
|
||||
if string.match(command, "^%s*$") then
|
||||
train.atc_command=nil
|
||||
return
|
||||
end
|
||||
--conditional statement?
|
||||
local is_cond, cond_applies
|
||||
local cond, rest=string.match(command, "^I([%+%-])(.+)$")
|
||||
if cond then
|
||||
is_cond=true
|
||||
if cond=="+" then
|
||||
cond_applies=train.atc_arrow
|
||||
end
|
||||
if cond=="-" then
|
||||
cond_applies=not train.atc_arrow
|
||||
end
|
||||
else
|
||||
cond, compare, rest=string.match(command, "^I([<>]=?)([0-9]+)(.+)$")
|
||||
if cond and compare then
|
||||
is_cond=true
|
||||
if cond=="<" then
|
||||
cond_applies=train.velocity<tonumber(compare)
|
||||
end
|
||||
if cond==">" then
|
||||
cond_applies=train.velocity>tonumber(compare)
|
||||
end
|
||||
if cond=="<=" then
|
||||
cond_applies=train.velocity<=tonumber(compare)
|
||||
end
|
||||
if cond==">=" then
|
||||
cond_applies=train.velocity>=tonumber(compare)
|
||||
end
|
||||
end
|
||||
end
|
||||
if is_cond then
|
||||
print("Evaluating if statement: "..command)
|
||||
print("Cond: "..(cond or "nil"))
|
||||
print("Applies: "..(cond_applies and "true" or "false"))
|
||||
print("Rest: "..rest)
|
||||
--find end of conditional statement
|
||||
local nest, pos, elsepos=0, 1
|
||||
while nest>=0 do
|
||||
if pos>#rest then
|
||||
print("ATC command syntax error: I statement not closed: "..command)
|
||||
atc.train_reset_command(id)
|
||||
return
|
||||
end
|
||||
local char=string.sub(rest, pos, pos)
|
||||
if char=="I" then
|
||||
nest=nest+1
|
||||
end
|
||||
if char==";" then
|
||||
nest=nest-1
|
||||
end
|
||||
if nest==0 and char=="E" then
|
||||
elsepos=pos+0
|
||||
end
|
||||
pos=pos+1
|
||||
end
|
||||
if not elsepos then elsepos=pos-1 end
|
||||
if cond_applies then
|
||||
command=string.sub(rest, 1, elsepos-1)..string.sub(rest, pos)
|
||||
else
|
||||
command=string.sub(rest, elsepos+1, pos-2)..string.sub(rest, pos)
|
||||
end
|
||||
print("Result: "..command)
|
||||
train.atc_command=command
|
||||
atc.execute_atc_command(id, train)
|
||||
return
|
||||
else
|
||||
for pattern, func in pairs(matchptn) do
|
||||
local match=string.match(command, "^"..pattern)
|
||||
if match then
|
||||
local patlen=func(id, train, match)
|
||||
|
||||
print("Executing: "..string.sub(command, 1, patlen))
|
||||
|
||||
train.atc_command=string.sub(command, patlen+1)
|
||||
if train.atc_delay<=0 and not train.atc_wait_finish then
|
||||
--continue (recursive, cmds shouldn't get too long, and it's a end-recursion.)
|
||||
atc.execute_atc_command(id, train)
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
print("ATC command parse error: "..command)
|
||||
atc.train_reset_command(id)
|
||||
end
|
||||
|
||||
--move table to desired place
|
||||
advtrains.atc=atc
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
ATC Command Syntax
|
||||
|
||||
A train runs the current ATC command once it receives it, including delayed instructions. If the train receives a new command, the current command is discarded.
|
||||
Spaces can be inserted into commands as needed and are ignored.
|
||||
|
||||
# simple commands:
|
||||
|
||||
S<[0-9]+ speed or 'M'>
|
||||
Set target speed of train to <speed>. Accelerates if slower, rolls if faster. 'M' means maximum speed.
|
||||
Execution of command continues immediately.
|
||||
|
||||
B<[0-9]+ speed>
|
||||
Brake until speed is reached. If faster, apply brakes, if slower, do nothing.
|
||||
Execution of command continues immediately.
|
||||
|
||||
Examples:
|
||||
SM : accelerate to maximum speed
|
||||
S0 : roll to stand
|
||||
B0 : brake to stand
|
||||
S0B3 or B3S0: brake to 3, then roll to stand.
|
||||
|
||||
W
|
||||
Wait until S and/or B commands reached the desired speed before continuing execution.
|
||||
|
||||
D<[0-9]+ time>
|
||||
Delay: Wait for time seconds before continuing execution.
|
||||
|
||||
R
|
||||
Reverse: change movement direction of train. ONLY WORKS WHEN TRAIN STANDS, else no-op.
|
||||
Use B0WR to definitely change direction.
|
||||
|
||||
Examples:
|
||||
B0 W R D10 SM
|
||||
Subway train stopping in dead end station and returning in opposite direction
|
||||
|
||||
# conditional statements:
|
||||
|
||||
I<condition><code>;
|
||||
Execute code only if condition applies
|
||||
I<condition><code1>E<code2>;
|
||||
Execute code1 only if condition applies, else execute code2
|
||||
|
||||
Conditions:
|
||||
+ / -
|
||||
Tests the train's movement direction against the arrow on the ATC rail: M+ is true when train drives in direction of arrow.
|
||||
|
||||
[</>/<=/>=][speed]
|
||||
Test if train's speed is greater or smaller than the given value
|
||||
|
||||
Examples:
|
||||
I- B0 W R ; S8
|
||||
If the train drives in the 'wrong' direction, stop and reverse; independently accelerate to speed 8 afterwards.
|
||||
|
||||
I<8 S8 ;
|
||||
If the train is slower than 8, accelerate to 8.
|
||||
|
||||
# ATC controller operation modes
|
||||
static: Only give 1 static command.
|
||||
mesecon: Give 2 different commands depending on if the controller is mesecon-powered or not
|
||||
digiline: Don't give any commands by itself. When a train passes, a digiline message in the form of "[+/-][speed]" is sent on the set channel (where +/- means the same as with conditions). Any digiline message sent to the controller will be interpreted as ATC command and sent to the train.
|
||||
|
||||
# Persistence
|
||||
ATC controllers that are configured as 'static' or 'mesecon' are persistent over mapblock unloads and will even command the train when the mapblock is unloaded. This is not possible with digilines since these do not work in unloaded mapchunks.
|
||||
|
||||
# LUA ATC controller (in development)
|
||||
The LUA ATC Controller will operate by using LUA code. All operations shown above will have a function equivalent. Additionally all LUA ATC controllers share an environment and setting signal and switch status will be possible to allow for complicated railway systems/fully automated subways a.s.o.
|
||||
Also planned:
|
||||
- digicompute add-on to allow computer access to the ATC environment (railway maps... ... ... ... ...)
|
After Width: | Height: | Size: 857 B |
After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
|
@ -129,7 +129,7 @@ function tp.bend_rail(originpos, conn, nnpref)
|
|||
return false
|
||||
end
|
||||
end
|
||||
function tp.placetrack(pos, nnpref)
|
||||
function tp.placetrack(pos, nnpref, placer, itemstack, pointed_thing)
|
||||
--1. find all rails that are likely to be connected
|
||||
local tr=tp.tracks[nnpref]
|
||||
local p_rails={}
|
||||
|
@ -140,9 +140,16 @@ function tp.placetrack(pos, nnpref)
|
|||
end
|
||||
if #p_rails==0 then
|
||||
minetest.set_node(pos, {name=nnpref.."_"..tr.default})
|
||||
if minetest.registered_nodes[nnpref.."_"..tr.default] and minetest.registered_nodes[nnpref.."_"..tr.default].after_place_node then
|
||||
minetest.registered_nodes[nnpref.."_"..tr.default].after_place_node(pos, placer, itemstack, pointed_thing)
|
||||
end
|
||||
elseif #p_rails==1 then
|
||||
tp.bend_rail(pos, p_rails[1], nnpref)
|
||||
minetest.set_node(pos, tr.single_conn[p_rails[1]])
|
||||
local nname=tr.single_conn[p_rails[1]].name
|
||||
if minetest.registered_nodes[nname] and minetest.registered_nodes[nname].after_place_node then
|
||||
minetest.registered_nodes[nname].after_place_node(pos, placer, itemstack, pointed_thing)
|
||||
end
|
||||
else
|
||||
--iterate subsets
|
||||
for k1, conn1 in ipairs(p_rails) do
|
||||
|
@ -152,6 +159,10 @@ function tp.placetrack(pos, nnpref)
|
|||
tp.bend_rail(pos, conn1, nnpref)
|
||||
tp.bend_rail(pos, conn2, nnpref)
|
||||
minetest.set_node(pos, tr.double_conn[conn1.."_"..conn2])
|
||||
local nname=tr.double_conn[conn1.."_"..conn2].name
|
||||
if minetest.registered_nodes[nname] and minetest.registered_nodes[nname].after_place_node then
|
||||
minetest.registered_nodes[nname].after_place_node(pos, placer, itemstack, pointed_thing)
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
|
@ -160,6 +171,10 @@ function tp.placetrack(pos, nnpref)
|
|||
--not found
|
||||
tp.bend_rail(pos, p_rails[1], nnpref)
|
||||
minetest.set_node(pos, tr.single_conn[p_rails[1]])
|
||||
local nname=tr.single_conn[p_rails[1]].name
|
||||
if minetest.registered_nodes[nname] and minetest.registered_nodes[nname].after_place_node then
|
||||
minetest.registered_nodes[nname].after_place_node(pos, placer, itemstack, pointed_thing)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -183,7 +198,7 @@ function tp.register_track_placer(nnprefix, imgprefix, dispname)
|
|||
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)
|
||||
tp.placetrack(pos, nnprefix, placer, itemstack, pointed_thing)
|
||||
if not minetest.setting_getbool("creative_mode") then
|
||||
itemstack:take_item()
|
||||
end
|
||||
|
@ -230,7 +245,7 @@ minetest.register_craftitem("advtrains:trackworker",{
|
|||
local modext=tp.tracks[nnprefix].twrotate[suffix]
|
||||
|
||||
if rotation==modext[#modext] then --increase param2
|
||||
minetest.set_node(pos, {name=nnprefix.."_"..suffix..modext[1], param2=(node.param2+1)%4})
|
||||
minetest.swap_node(pos, {name=nnprefix.."_"..suffix..modext[1], param2=(node.param2+1)%4})
|
||||
return
|
||||
else
|
||||
local modpos
|
||||
|
@ -239,7 +254,7 @@ minetest.register_craftitem("advtrains:trackworker",{
|
|||
minetest.chat_send_player(placer:get_player_name(), "This node can't be rotated using the trackworker!")
|
||||
return
|
||||
end
|
||||
minetest.set_node(pos, {name=nnprefix.."_"..suffix..modext[modpos+1], param2=node.param2})
|
||||
minetest.swap_node(pos, {name=nnprefix.."_"..suffix..modext[modpos+1], param2=node.param2})
|
||||
end
|
||||
advtrains.invalidate_all_paths()
|
||||
end
|
||||
|
@ -269,7 +284,7 @@ minetest.register_craftitem("advtrains:trackworker",{
|
|||
end
|
||||
end
|
||||
local nextsuffix=tp.tracks[nnprefix].twcycle[suffix]
|
||||
minetest.set_node(pos, {name=nnprefix.."_"..nextsuffix..rotation, param2=node.param2})
|
||||
minetest.swap_node(pos, {name=nnprefix.."_"..nextsuffix..rotation, param2=node.param2})
|
||||
--invalidate trains
|
||||
advtrains.invalidate_all_paths()
|
||||
else
|
||||
|
|
|
@ -36,7 +36,8 @@ advtrains.all_tracktypes={}
|
|||
--definition preparation
|
||||
local function conns(c1, c2, r1, r2, rh, rots) return {conn1=c1, conn2=c2, rely1=r1, rely2=r2, railheight=rh} end
|
||||
|
||||
local t_30deg={
|
||||
local ap={}
|
||||
ap.t_30deg={
|
||||
regstep=1,
|
||||
variant={
|
||||
st=conns(0,8),
|
||||
|
@ -110,7 +111,7 @@ local t_30deg={
|
|||
rotation={"", "_30", "_45", "_60"},
|
||||
increativeinv={},
|
||||
}
|
||||
local t_30deg_straightonly={
|
||||
ap.t_30deg_straightonly={
|
||||
regstep=1,
|
||||
variant={
|
||||
st=conns(0,8),
|
||||
|
@ -135,7 +136,7 @@ local t_30deg_straightonly={
|
|||
rotation={"", "_30", "_45", "_60"},
|
||||
increativeinv={st},
|
||||
}
|
||||
local t_30deg_straightonly_noplacer={
|
||||
ap.t_30deg_straightonly_noplacer={
|
||||
regstep=1,
|
||||
variant={
|
||||
st=conns(0,8),
|
||||
|
@ -160,7 +161,7 @@ local t_30deg_straightonly_noplacer={
|
|||
rotation={"", "_30", "_45", "_60"},
|
||||
increativeinv={st},
|
||||
}
|
||||
local t_45deg={
|
||||
ap.t_45deg={
|
||||
regstep=2,
|
||||
variant={
|
||||
st=conns(0,8),
|
||||
|
@ -215,6 +216,7 @@ local t_45deg={
|
|||
rotation={"", "_45"},
|
||||
increativeinv={vst1=true, vst2=true}
|
||||
}
|
||||
advtrains.trackpresets = ap
|
||||
|
||||
--definition format: ([] optional)
|
||||
--[[{
|
||||
|
@ -302,6 +304,9 @@ function advtrains.register_tracks(tracktype, def, preset)
|
|||
after_place_node=function(pos)
|
||||
advtrains.reset_trackdb_position(pos)
|
||||
end,
|
||||
on_place_rail=function(pos)
|
||||
advtrains.reset_trackdb_position(pos)
|
||||
end,
|
||||
}, def.common or {})
|
||||
--make trackplacer base def
|
||||
advtrains.trackplacer.register_tracktype(def.nodename_prefix, preset.tpdefault)
|
||||
|
@ -386,10 +391,10 @@ advtrains.detector.clean_step_before = false
|
|||
--The entry already being contained in advtrains.detector.on_node_restore will not trigger an on_train_enter event on the node. (when path is reset, this is saved).
|
||||
function advtrains.detector.enter_node(pos, train_id)
|
||||
local pts = minetest.pos_to_string(advtrains.round_vector_floor_y(pos))
|
||||
print("enterNode "..pts.." "..sid(train_id))
|
||||
--print("enterNode "..pts.." "..sid(train_id))
|
||||
if advtrains.detector.on_node[pts] then
|
||||
if advtrains.trains[advtrains.detector.on_node[pts]] then
|
||||
print(""..pts.." already occupied")
|
||||
--print(""..pts.." already occupied")
|
||||
return false
|
||||
else
|
||||
advtrains.detector.leave_node(pos, advtrains.detector.on_node[pts])
|
||||
|
@ -405,9 +410,9 @@ function advtrains.detector.enter_node(pos, train_id)
|
|||
end
|
||||
function advtrains.detector.leave_node(pos, train_id)
|
||||
local pts = minetest.pos_to_string(advtrains.round_vector_floor_y(pos))
|
||||
print("leaveNode "..pts.." "..sid(train_id))
|
||||
--print("leaveNode "..pts.." "..sid(train_id))
|
||||
if not advtrains.detector.on_node[pts] then
|
||||
print(""..pts.." leave: nothing here")
|
||||
--print(""..pts.." leave: nothing here")
|
||||
return false
|
||||
end
|
||||
if advtrains.detector.on_node[pts]==train_id then
|
||||
|
@ -415,7 +420,7 @@ function advtrains.detector.leave_node(pos, train_id)
|
|||
advtrains.detector.on_node[pts]=nil
|
||||
else
|
||||
if advtrains.trains[advtrains.detector.on_node[pts]] then
|
||||
print(""..pts.." occupied by another train")
|
||||
--print(""..pts.." occupied by another train")
|
||||
return false
|
||||
else
|
||||
advtrains.detector.leave_node(pos, advtrains.detector.on_node[pts])
|
||||
|
@ -446,7 +451,10 @@ function advtrains.detector.call_enter_callback(pos, train_id)
|
|||
local mregnode=minetest.registered_nodes[node.name]
|
||||
if mregnode and mregnode.advtrains and mregnode.advtrains.on_train_enter then
|
||||
mregnode.advtrains.on_train_enter(pos, train_id)
|
||||
end
|
||||
end
|
||||
|
||||
--atc code wants to be notified too
|
||||
advtrains.atc.trigger_controller_train_enter(pos, train_id)
|
||||
end
|
||||
function advtrains.detector.call_leave_callback(pos, train_id)
|
||||
--print("instructed to call leave calback")
|
||||
|
@ -576,7 +584,7 @@ advtrains.register_tracks("regular", {
|
|||
shared_model="trackplane.b3d",
|
||||
description="Deprecated Track",
|
||||
formats={vst1={}, vst2={}},
|
||||
}, t_45deg)
|
||||
}, ap.t_45deg)
|
||||
|
||||
|
||||
advtrains.register_tracks("default", {
|
||||
|
@ -587,7 +595,7 @@ advtrains.register_tracks("default", {
|
|||
shared_texture="advtrains_dtrack_rail.png",
|
||||
description="Track",
|
||||
formats={vst1={true, false, true}, vst2={true, false, true}, vst31={true}, vst32={true}, vst33={true}},
|
||||
}, t_30deg)
|
||||
}, ap.t_30deg)
|
||||
|
||||
--bumpers
|
||||
advtrains.register_tracks("default", {
|
||||
|
@ -598,7 +606,7 @@ advtrains.register_tracks("default", {
|
|||
shared_texture="advtrains_dtrack_rail.png",
|
||||
description="Bumper",
|
||||
formats={},
|
||||
}, t_30deg_straightonly)
|
||||
}, ap.t_30deg_straightonly)
|
||||
--legacy bumpers
|
||||
for _,rot in ipairs({"", "_30", "_45", "_60"}) do
|
||||
minetest.register_alias("advtrains:dtrack_bumper"..rot, "advtrains:dtrack_bumper_st"..rot)
|
||||
|
@ -629,7 +637,7 @@ if mesecon then
|
|||
}
|
||||
}
|
||||
end
|
||||
}, t_30deg_straightonly)
|
||||
}, ap.t_30deg_straightonly)
|
||||
advtrains.register_tracks("default", {
|
||||
nodename_prefix="advtrains:dtrack_detector_on",
|
||||
texture_prefix="advtrains_dtrack_detector",
|
||||
|
@ -654,7 +662,7 @@ if mesecon then
|
|||
}
|
||||
}
|
||||
end
|
||||
}, t_30deg_straightonly_noplacer)
|
||||
}, ap.t_30deg_straightonly_noplacer)
|
||||
end
|
||||
--TODO legacy
|
||||
--I know lbms are better for this purpose
|
||||
|
|
|
@ -11,7 +11,7 @@ local mletter={[1]="F", [-1]="R", [0]="N"}
|
|||
function advtrains.on_control_change(pc, train, flip)
|
||||
if pc.sneak then
|
||||
if pc.up then
|
||||
train.tarvelocity = advtrains.all_traintypes[train.traintype].max_speed or 10
|
||||
train.tarvelocity = train.max_speed or 10
|
||||
end
|
||||
if pc.down then
|
||||
train.tarvelocity = 0
|
||||
|
|
|
@ -119,6 +119,7 @@ advtrains.save = function()
|
|||
file:close()
|
||||
|
||||
advtrains.save_trackdb()
|
||||
advtrains.atc.save()
|
||||
end
|
||||
minetest.register_on_shutdown(advtrains.save)
|
||||
|
||||
|
@ -302,6 +303,24 @@ function advtrains.train_step(id, train, dtime)
|
|||
if train.locomotives_in_train==0 then
|
||||
train.tarvelocity=0
|
||||
end
|
||||
|
||||
--interpret ATC command
|
||||
if train.atc_brake_target and train.atc_brake_target>=train.velocity then
|
||||
train.atc_brake_target=nil
|
||||
end
|
||||
if train.atc_wait_finish then
|
||||
if not train.atc_brake_target and train.velocity==train.tarvelocity then
|
||||
train.atc_wait_finish=nil
|
||||
end
|
||||
end
|
||||
if train.atc_command then
|
||||
if train.atc_delay<=0 and not train.atc_wait_finish then
|
||||
advtrains.atc.execute_atc_command(id, train)
|
||||
else
|
||||
train.atc_delay=train.atc_delay-dtime
|
||||
end
|
||||
end
|
||||
|
||||
--make brake adjust the tarvelocity if necessary
|
||||
if train.brake and (math.ceil(train.velocity)-1)<train.tarvelocity then
|
||||
train.tarvelocity=math.max((math.ceil(train.velocity)-1), 0)
|
||||
|
@ -318,7 +337,7 @@ function advtrains.train_step(id, train, dtime)
|
|||
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)))
|
||||
elseif train.brake then
|
||||
elseif train.brake or (train.atc_brake_target and train.atc_brake_target<train.velocity) then
|
||||
--print("braking with default force")
|
||||
--no math.min, because it can grow beyond tarvelocity, see up there
|
||||
--dont worry, it will never fall below zero.
|
||||
|
|