Issue: what is the logic for train carriage order when using 'connect stock'? A train and all the wagons can reorient (carriage.orientation) when making a connection - including a reversal of all the carriages in train.carriages. This causes problems if a uniform carriage order is necessary, as in the comfy scenario "Mountain Fortress v3"
I've assumed some agnostic methods would be:
/1. the least x then least y carriage orientation, or reversed, least y then least x
/2. first locomotive orientation based on position
/3. orientation of the frontmost entity in 'leading' locomotive's orientation
/4. something with selected train orientation when 'connect stock' is used
But none of these seem to be it.
Included is an example save. Image for reference:
trains.jpg (416.56 KiB) Viewed 2177 times
On the bottom loop for example, if the train is on the top side, disconnecting the carriages and reversing either locomotive then reconnecting everything has the effect of not re-orienting the train, but on the bottom the train carriage order reverses when either locomotive is rotated.
Here is an example command to show which things are changing:
/c
local train = game.player.selected.train
local string = ""
for i, carriage in pairs(train.carriages) do
string = string.."\n"..i..": "..carriage.name.." orientation: "..carriage.orientation
end
game.print(string)
Re: Train carriage order logic
Posted: Sun Sep 06, 2020 2:21 pm
by boskid
I would say the best approach would be to make your code to be not dependent on any particular rolling stock order.
From the code (1.0), when train is created (including cases of connecting rollling stocks and disconnecting rolling stocks when the train is rebuilt), there is a check to decide which border rolling stock will be the front of a train:
- take the end that has most of the locomotives pointing in that direction and make it front. In case of same amount:
- take the end that has X position lower and use it as a front. In case of same x position:
- take the end that has Y position lower and use it as a front. In case of same y position - it should be only possible when a train has only 1 rolling stock that is not a locomotive.
However keep in mind this is nothing guaranteed and may change any time.
Thank you for your reply. There was an idea that since one of the locomotives is special in this case, a solution could be find which way it faces when disconnected (maybe update the record if someone rotates it), and recreating the original carriage order by decrementing/incrementing in the direction of "front" in a lua table. Any other trains would be forced into a "least y then least x locomotive direction" order or something, to at least make it more predictable.
Re: Train carriage order logic
Posted: Sat Sep 12, 2020 7:48 am
by boskid
I have added LuaEntity::get_connected_rolling_stock() (for 1.1.0 release) that allows implementing custom iteration over rolling stocks without using LuaTrain::carriages. If you have a rolling stock and want to know what is connected to its back, this will simply allow this, and for going farther than 1 rolling stock, there is also second value that says to which end of rolling stock this was connected, so you can flip it to the other side and again call get_connected_rolling_stock to get next rolling stock. Will it be good enough to solve your problem?
89144.png (20.22 KiB) Viewed 2110 times
It is O(1) and will return exactly the same values as following O(N) workaround code:
/c
local function get_connected_rolling_stock(entity, direction)
local carriages = entity.train.carriages
local first_stock, second_stock
for k, v in pairs(carriages) do
if v == entity then
first_stock = carriages[k - 1]
second_stock = carriages[k + 1]
break
end
end
if not first_stock then
first_stock, second_stock = second_stock, nil
end
if not first_stock then
return nil
end
local angle = math.atan2(-(entity.position.x - first_stock.position.x), entity.position.y - first_stock.position.y)/(2*math.pi) - entity.orientation
if direction == defines.rail_direction.back then
angle = angle + 0.5
end
while angle < -0.5 do
angle = angle + 1
end
while angle > 0.5 do
angle = angle - 1
end
local connected_stock
if angle > -0.25 and angle < 0.25 then
connected_stock = first_stock
else
connected_stock = second_stock
end
if not connected_stock then
return nil
end
angle = math.atan2(-(connected_stock.position.x - entity.position.x), connected_stock.position.y - entity.position.y)/(2*math.pi) - connected_stock.orientation
while angle < -0.5 do
angle = angle + 1
end
while angle > 0.5 do
angle = angle - 1
end
local joint_of_connected_stock
if angle > -0.25 and angle < 0.25 then
joint_of_connected_stock = defines.rail_direction.front
else
joint_of_connected_stock = defines.rail_direction.back
end
return connected_stock, joint_of_connected_stock
end
local stock, joint_of_connected_stock = get_connected_rolling_stock(game.player.selected, defines.rail_direction.front)
if stock then
game.print("ref stock.type: "..stock.type)
game.print("ref joint_of_connected_stock: "..joint_of_connected_stock)
else
game.print("ref no stock")
end
local stock, joint_of_connected_stock = game.player.selected.get_connected_rolling_stock(defines.rail_direction.front)
if stock then
game.print("native stock.type: "..stock.type)
game.print("native joint_of_connected_stock: "..joint_of_connected_stock)
else
game.print("native no stock")
end
lib.print_carriages = function(event)
local player = game.players[1]
if player == nil then return end
local ent = player.selected
if ent == nil then return end
local train = ent.train
if train == nil then return end
local outstring = ""
for _, carriage in pairs(train.carriages) do
outstring = outstring.."\nPerspective: "..carriage.name
local stock, joint_of_connected_stock = get_connected_rolling_stock(carriage, defines.rail_direction.front)
if stock then
outstring = outstring.."\nfront stock.type: "..stock.type
outstring = outstring.."\nfront joint_of_connected_stock: "..joint_of_connected_stock
else
outstring = outstring.."\nfront no stock"
end
stock, joint_of_connected_stock = get_connected_rolling_stock(carriage, defines.rail_direction.back)
if stock then
outstring = outstring.."\nback stock.type: "..stock.type
outstring = outstring.."\nback joint_of_connected_stock: "..joint_of_connected_stock
else
outstring = outstring.."\nback no stock"
end
end
log(outstring)
end
Perspective: cargo-wagon
front stock.type: locomotive
front joint_of_connected_stock: 0
back no stock
Perspective: locomotive
front stock.type: cargo-wagon
front joint_of_connected_stock: 0
back no stock
I think this is all well and good, but I'll need some extra logic: start with a specific carriage, then get an entity in the front direction. If the "front" of that entity returns the carriage I am basing my direction on, reverse the direction, and get the next entity. Repeat until nil. This is the "front" of the train from the perspective of the original entity's facing. I can then reverse the process, inserting each entity into a table before I go on to the next, flipping the direction if needed. The hardest part I imagined originally was having a way to determine which connected stocks are "forwards" and "backwards", with consideration for how the game defines orientations and the concept of carriage direction being a bit messy. Thanks.
A quick additional question: is there a license restriction for your function? If I were to use the code before 1.1, it may end up in a GPLv3 scenario, MIT, or Unlicensed (most likely licenses). Nothing that says "this is mine", but I would like to be careful, you know?
Re: Train carriage order logic
Posted: Mon Sep 14, 2020 6:24 am
by boskid
Honktown wrote: Mon Sep 14, 2020 5:51 am
A quick additional question: is there a license restriction for your function? If I were to use the code before 1.1, it may end up in a GPLv3 scenario, MIT, or Unlicensed (most likely licenses). Nothing that says "this is mine", but I would like to be careful, you know?
I dont care, feel free to use with any license.
-- edit:
Every rolling stock has front and back, in oddtrain.jpg you have locomotive's front connected to cargo wagon's front (joint_of_connected_stock==0 which means front) so this is what the results will be. In all cases front joint is forward based on the orientation and back joint is behind based on orientation. If you do not have some super long rolling stocks that would bend on curved rails so much that position of front rolling stock would go behind the decision line this function should work correct (native function will always work correct).
Re: Train carriage order logic
Posted: Mon Sep 14, 2020 2:34 pm
by Honktown
...I was looking at this before bed and was way overthinking it. Once I have the carriage in front of the locomotive, the desired order of train.carriages is known. Increment/decrement from there. This should solve the problem.