Compare commits

...

5 Commits

Author SHA1 Message Date
orwell96 3cb21a80dc Wagons: Add two-pos wheel positioning logic for more realistic look of long wagons
Adds field "wheel_positions" in wagon definition
2022-02-09 23:06:02 +01:00
orwell96 ef8391a60d atc_reset(): Removed unused string parameter and also clear ATC autocouple flag (Cpl)
Fixes H#189 and H#190
2021-12-31 16:04:04 +01:00
Maverick2797 78e0c650e3 Load/Unload Track FC Integration
- Set a freight code in the track to whitelist only wagons with that FC as the current one

 - Set as # to disable the track

 - Set as a blank field (default) to allow all wagons to activate, irrespective of their FC
2021-12-29 21:21:37 +01:00
Maverick2797 7db61e9890 Automatically group wagons and locomotives based on definition values
Based on the advtrains wiki groups:
  Type		Group			Classifier
- Locomotives:	group:at_loco		is_locomotive = true
- Controllable:	group:at_control	seat_groups.dstand
- Passengers:	group:at_pax		seat_groups.pass
- Freight:	group:at_freight	has_inventory = true
2021-12-29 21:17:34 +01:00
Maverick2797 23d524df71 Luaautomation: add section_occupancy()
Returns a table of train ids for the specified section.
Returns nil if the section id is not provided..
Returns false if the section id is invalid.
Returns an empty table if the section id is valid but empty of trains.
2021-12-29 21:16:32 +01:00
7 changed files with 198 additions and 82 deletions

View File

@ -85,6 +85,13 @@ advtrains.register_wagon(name, prototype, description, inventory_image)
wagon_span=2,
^- How far this wagon extends from its base position. Is the half of the wagon length.
^- Used to determine in which distance the other wagons have to be positioned. Will require tweaking.
wheel_positions = {1.5, -1.5},
^- Optional: if defined, the wagon will be placed so that these 2 wheel positions are on the track
^- This parameter is recommended for long wagons (wagon_span >= 2).
^- The position is a distance relative to the center of the wagon.
^- Must have exactly 2 entries, corresponding to the front (1) and rear (2) wheel of the wagon object. 1st must be greater than 2nd.
^- If not provided, the simple 1-position positioning logic will be used (wagon is positioned with the center on the track)
extent_h = 1,
^- Determines the collision box extent in x/z direction. Defaults to 1 (=3x3)
^- The actual bounding box size is (extent_h*2)+1, so 0 means 1x1, 1 means 3x3 and 2 means 5x5

View File

@ -93,6 +93,7 @@ function atc.train_reset_command(train, keep_tarvel)
train.atc_delay=nil
train.atc_brake_target=nil
train.atc_wait_finish=nil
train.atc_wait_autocouple=nil
train.atc_arrow=nil
if not keep_tarvel then
train.tarvelocity=nil

View File

@ -413,13 +413,36 @@ function wagon:on_step(dtime)
end
-- Calculate new position, yaw and direction vector
-- note: "index" is needed to be the center index, required by door code
local index = advtrains.path_get_index_by_offset(train, train.index, -data.pos_in_train)
local pos, yaw, npos, npos2 = advtrains.path_get_interpolated(train, index)
local vdir = vector.normalize(vector.subtract(npos2, npos))
local pos, yaw, npos, npos2, vdir
-- use new position logic?
if self.wheel_positions then
-- request two positions, calculate difference and yaw from this
-- depending on flipstate, need to invert wheel pos indices -> wheelpos * fct
local index1 = advtrains.path_get_index_by_offset(train, index, self.wheel_positions[1] * fct)
local index2 = advtrains.path_get_index_by_offset(train, index, self.wheel_positions[2] * fct)
local pos1 = advtrains.path_get_interpolated(train, index1)
local pos2 = advtrains.path_get_interpolated(train, index2)
npos = advtrains.path_get(train, atfloor(index)) -- need npos just for node loaded check
-- calculate center of 2 positions and vdir vector
pos = advtrains.pos_median(pos1, pos2)
if data.wagon_flipped then
vdir = vector.normalize(vector.subtract(pos2, pos1))
else
vdir = vector.normalize(vector.subtract(pos1, pos2))
end
yaw = math.atan2(-vdir.x, vdir.z)
else
--old position logic (for small wagons): use center index and just get position
pos, yaw, npos, npos2 = advtrains.path_get_interpolated(train, index)
vdir = vector.normalize(vector.subtract(npos2, npos))
end
--automatic get_on
--needs to know index and path
if self.door_entry and train.door_open and train.door_open~=0 and train.velocity==0 then
if train.velocity==0 and self.door_entry and train.door_open and train.door_open~=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
@ -470,29 +493,34 @@ function wagon:on_step(dtime)
end
end
--DisCouple
-- Spawn discouple object when train stands, in all other cases remove it.
-- FIX: Need to do this after the yaw calculation
if is_in_loaded_area and data.pos_in_trainparts and data.pos_in_trainparts>1 then
if train.velocity==0 then
if not self.discouple or not self.discouple.object:get_yaw() then
atprint(self.id,"trying to spawn discouple")
local dcpl_pos = vector.add(pos, {y=0, x=-math.sin(yaw)*self.wagon_span, z=math.cos(yaw)*self.wagon_span})
local object=minetest.add_entity(dcpl_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
end
end
else
if self.discouple and self.discouple.object:get_yaw() then
self.discouple.object:remove()
atprint(self.id," removing discouple")
if train.velocity==0 and is_in_loaded_area and data.pos_in_trainparts and data.pos_in_trainparts>1 then
if not self.discouple or not self.discouple.object:get_yaw() then
atprint(self.id,"trying to spawn discouple")
local dcpl_pos = vector.add(pos, {y=0, x=-math.sin(yaw)*self.wagon_span, z=math.cos(yaw)*self.wagon_span})
local object=minetest.add_entity(dcpl_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
end
end
else
if self.discouple and self.discouple.object:get_yaw() then
self.discouple.object:remove()
atprint(self.id," removing discouple")
end
end
-- object yaw (corrected by flipstate)
local oyaw = yaw
if data.wagon_flipped then
oyaw = yaw + math.pi
end
train.debug = "yaw "..yaw.." oyaw "..oyaw.." flip "..(data.wagon_flipped and "yes" or "no")
--FIX: use index of the wagon, not of the train.
local velocity = train.velocity * advtrains.global_slowdown
@ -500,10 +528,6 @@ function wagon:on_step(dtime)
local velocityvec = vector.multiply(vdir, velocity)
local accelerationvec = vector.multiply(vdir, acceleration)
if data.wagon_flipped then
yaw=yaw+math.pi
end
-- this timer runs off every 2 seconds.
self.updatepct_timer=(self.updatepct_timer or 0)-dtime
local updatepct_timer_elapsed = self.updatepct_timer<=0
@ -540,19 +564,19 @@ function wagon:on_step(dtime)
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.old_yaw~=oyaw
or updatepct_timer_elapsed then--only send update packet if something changed
self.object:set_pos(pos)
self.object:set_velocity(velocityvec)
self.object:set_acceleration(accelerationvec)
if #self.seats > 0 and self.old_yaw ~= yaw then
if #self.seats > 0 and self.old_yaw ~= oyaw then
if not self.player_yaw then
self.player_yaw = {}
end
if not self.old_yaw then
self.old_yaw=yaw
self.old_yaw=oyaw
end
for _,name in pairs(data.seatp) do
local p = minetest.get_player_by_name(name)
@ -562,11 +586,11 @@ function wagon:on_step(dtime)
self.player_yaw[name] = p:get_look_horizontal()-self.old_yaw
end
-- set player looking direction using calculated offset
p:set_look_horizontal((self.player_yaw[name] or 0)+yaw)
p:set_look_horizontal((self.player_yaw[name] or 0)+oyaw)
end
end
self.turning = true
elseif self.old_yaw == yaw then
elseif self.old_yaw == oyaw then
-- train is no longer turning
self.turning = false
end
@ -576,9 +600,9 @@ function wagon:on_step(dtime)
if data.wagon_flipped then
pitch = -pitch
end
self.object:set_rotation({x=pitch, y=yaw, z=0})
self.object:set_rotation({x=pitch, y=oyaw, z=0})
else
self.object:set_yaw(yaw)
self.object:set_yaw(oyaw)
end
if self.update_animation then
@ -597,7 +621,7 @@ function wagon:on_step(dtime)
self.old_velocity_vector=velocityvec
self.old_velocity = train.velocity
self.old_acceleration_vector=accelerationvec
self.old_yaw=yaw
self.old_yaw=oyaw
atprintbm("wagon step", t)
end
@ -1316,14 +1340,23 @@ function advtrains.register_wagon(sysname_p, prototype, desc, inv_img, nincreati
minetest.register_entity(":"..sysname,prototype)
advtrains.wagon_prototypes[sysname] = prototype
--group classification to make recipe searching easier
local wagon_groups = { not_in_creative_inventory = nincreative and 1 or 0}
if prototype.is_locomotive then wagon_groups['at_loco'] = 1 end
if prototype.seat_groups then
if prototype.seat_groups.dstand then wagon_groups['at_control'] = 1 end
if prototype.seat_groups.pass then wagon_groups['at_pax'] = 1 end
end
if prototype.has_inventory then wagon_groups['at_freight'] = 1 end
minetest.register_craftitem(":"..sysname, {
description = desc,
inventory_image = inv_img,
wield_image = inv_img,
stack_max = 1,
groups = { not_in_creative_inventory = nincreative and 1 or 0},
groups = wagon_groups,
on_place = function(itemstack, placer, pointed_thing)
if not pointed_thing.type == "node" then
return

5
advtrains_luaautomation/README.md Normal file → Executable file
View File

@ -314,13 +314,16 @@ Deprecated:
#### Interlocking
This additional function is available when advtrains_interlocking is enabled:
These additional functions are available when advtrains_interlocking is enabled:
- `atc_set_ars_disable(boolean)`
Disables (true) or enables (false) the use of ARS for this train. The train will not trigger ARS (automatic route setting) on signals then.
Note: If you want to disable ARS from an approach callback, the call to `atc_set_ars_disable(true)` *must* happen during the approach callback, and may not be deferred to an interrupt(). Else the train might trigger an ARS before the interrupt fires.
- `section_occupancy(section_id)`
Returns a table of train ids for the specified section, nil if no section id is provided, false if the section id is invalid, an empty table if the section id is valid but empty of trains.
#### Approach callbacks
The LuaATC interface provides a way to hook into the approach callback system, which is for example used in the TSR rails (provided by advtrains_interlocking) or the station tracks (provided by advtrains_lines). However, for compatibility reasons, this behavior needs to be explicitly enabled.

View File

@ -130,9 +130,8 @@ function r.fire_event(pos, evtdata, appr_internal)
get_rc = function()
return train.routingcode
end,
atc_reset = function(cmd)
atc_reset = function()
if not train_id then return false end
assertt(cmd, "string")
advtrains.atc.train_reset_command(train)
return true
end,

12
advtrains_luaautomation/environment.lua Normal file → Executable file
View File

@ -223,6 +223,18 @@ if advtrains.interlocking then
local pos = atlatc.pcnaming.resolve_pos(signal)
return advtrains.interlocking.signal_set_aspect(pos)
end
--section_occupancy()
static_env.section_occupancy = function(ts_id)
if not ts_id then return nil end
ts_id = tostring(ts_id)
local response = advtrains.interlocking.db.get_ts(ts_id)
if response == nil then
return false
else
return response.trains
end
end
end
-- Lines-specific:

View File

@ -216,39 +216,95 @@ local function get_far_node(pos)
return node
end
local function show_fc_formspec(pos,player)
local pname = player:get_player_name()
if minetest.is_protected(pos,pname) then
minetest.chat_send_player(pname, "Position is protected!")
return
end
local meta = minetest.get_meta(pos)
local fc = meta:get_string("fc") or ""
local form = 'formspec_version[4]'..
'size[10,5]'..
'label[0.5,0.4;Advtrains Loading/Unloading Track]'..
'label[0.5,1.1;Set the code to match against the wagon\'s freight code]'..
'label[0.5,1.6;A blank field matches all wagons (default)]'..
'label[0.5,2.1;Use code # to disable the track section]'..
'field[0.5,3;5.5,1;fc;FC;'..minetest.formspec_escape(fc)..']'..
'button[6.5,3;3,1;save;Submit]'
minetest.show_formspec(pname, "at_load_unload_"..advtrains.encode_pos(pos), form)
end
minetest.register_on_player_receive_fields(function(player, formname, fields)
local pname = player:get_player_name()
local pe = string.match(formname, "^at_load_unload_(............)$")
local pos = advtrains.decode_pos(pe)
if pos then
if minetest.is_protected(pos, pname) then
minetest.chat_send_player(pname, "Position is protected!")
return
end
if fields.save then
minetest.get_meta(pos):set_string("fc",tostring(fields.fc))
minetest.chat_send_player(pname,"Freight code set: "..tostring(fields.fc))
show_fc_formspec(pos,player)
end
end
end)
local function train_load(pos, train_id, unload)
local train=advtrains.trains[train_id]
local below = get_far_node({x=pos.x, y=pos.y-1, z=pos.z})
if not string.match(below.name, "chest") then
atprint("this is not a chest! at "..minetest.pos_to_string(pos))
return
end
local inv = minetest.get_inventory({type="node", pos={x=pos.x, y=pos.y-1, z=pos.z}})
if inv and train.velocity < 2 then
for k, v in ipairs(train.trainparts) do
local train=advtrains.trains[train_id]
local below = get_far_node({x=pos.x, y=pos.y-1, z=pos.z})
if not string.match(below.name, "chest") then
atprint("this is not a chest! at "..minetest.pos_to_string(pos))
return
end
local node_fc = minetest.get_meta(pos):get_string("fc") or ""
if node_fc == "#" then
--track section is disabled
return
end
local inv = minetest.get_inventory({type="node", pos={x=pos.x, y=pos.y-1, z=pos.z}})
if inv and train.velocity < 2 then
for k, v in ipairs(train.trainparts) do
local i=minetest.get_inventory({type="detached", name="advtrains_wgn_"..v})
if i and i:get_list("box") then
if not unload then
for _, item in ipairs(inv:get_list("main")) do
if i:get_list("box") and i:room_for_item("box", item) then
i:add_item("box", item)
inv:remove_item("main", item)
local wagon_data = advtrains.wagons[v]
local wagon_fc
if wagon_data.fc then
if not wagon_data.fcind then wagon_data.fcind = 1 end
wagon_fc = tostring(wagon_data.fc[wagon_data.fcind]) or ""
end
if node_fc == "" or wagon_fc == node_fc then
if not unload then
for _, item in ipairs(inv:get_list("main")) do
if i:get_list("box") and i:room_for_item("box", item) then
i:add_item("box", item)
inv:remove_item("main", item)
end
end
end
else
for _, item in ipairs(i:get_list("box")) do
if inv:get_list("main") and inv:room_for_item("main", item) then
i:remove_item("box", item)
inv:add_item("main", item)
else
for _, item in ipairs(i:get_list("box")) do
if inv:get_list("main") and inv:room_for_item("main", item) then
i:remove_item("box", item)
inv:add_item("main", item)
end
end
end
end
end
end
end
end
end
end
@ -262,15 +318,18 @@ advtrains.register_tracks("default", {
formats={},
get_additional_definiton = function(def, preset, suffix, rotation)
return {
after_dig_node=function(pos)
advtrains.invalidate_all_paths()
advtrains.ndb.clear(pos)
end,
advtrains = {
on_train_enter = function(pos, train_id)
train_load(pos, train_id, true)
end,
},
after_dig_node=function(pos)
advtrains.invalidate_all_paths()
advtrains.ndb.clear(pos)
end,
on_rightclick = function(pos, node, player)
show_fc_formspec(pos, player)
end,
advtrains = {
on_train_enter = function(pos, train_id)
train_load(pos, train_id, true)
end,
},
}
end
}, advtrains.trackpresets.t_30deg_straightonly)
@ -284,16 +343,18 @@ advtrains.register_tracks("default", {
formats={},
get_additional_definiton = function(def, preset, suffix, rotation)
return {
after_dig_node=function(pos)
advtrains.invalidate_all_paths()
advtrains.ndb.clear(pos)
end,
advtrains = {
on_train_enter = function(pos, train_id)
train_load(pos, train_id, false)
end,
},
after_dig_node=function(pos)
advtrains.invalidate_all_paths()
advtrains.ndb.clear(pos)
end,
on_rightclick = function(pos, node, player)
show_fc_formspec(pos, player)
end,
advtrains = {
on_train_enter = function(pos, train_id)
train_load(pos, train_id, false)
end,
},
}
end
}, advtrains.trackpresets.t_30deg_straightonly)