Skip to content

Commit

Permalink
Added ability to attach lights to shape sub-objects, some refactoring…
Browse files Browse the repository at this point in the history
…, and entry in news.rst
  • Loading branch information
SteelFill committed Feb 21, 2024
1 parent fde18cf commit a948dbe
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 62 deletions.
43 changes: 43 additions & 0 deletions Source/Documentation/Manual/features-rollingstock.rst
Expand Up @@ -91,6 +91,49 @@ implementation takes up much less space and guarentees that both modes of the
light have the exact same ``States``. There is no hard limit on the number
of conditions a light can have.

Lights attached to shape sub-objects
------------------------------------

The standard lighting configuration attaches all lights to the main body of the
locomotive or wagon. While this allows lights to move and rotate as the vehicle
itself moves, the approach has proven insufficient for more complicated rail
vehicles such as articulated steam locomotives.

.. index::
single: ShapeHierarchy

To facilitate lighting on such locomotives and wagons, Open Rails now allows
for attachment of lights to any sub-object of the shape file. With the
``ShapeHierarchy`` token placed in a ``Light ()`` block, the object the light
will rotate and translate with can be defined using the hierarchy name of said
object. Tools such as Shape Viewer can be used to determine the hierarchy name
of a particular object in the shape file. For example, *"BOGIE1"* is the standard
name for the frontmost bogie. A light attached to this bogie could be created
like so::

Light (
comment( CNDR Side Front Truck Light )
ShapeHierarchy ( "BOGIE1" )
States ( 1
State (
LightColour ( 91fedf91 )
Position ( -1.427 0.583 -0.330 )
Azimuth ( -90 -90 -90 )
Radius ( 0.2 )
)
)
)

Be aware that the ``Position`` of a light is measured relative to the center of
the object to which the light is attached, not to the center of the locomotive
itself. Furthermore, the naming of shape parts is not consistent between all
shape files. If the shape name entered in ``ShapeHierarchy`` is invalid, a
warning will be produced in the log file and the light will attach to the
main body of the locomotive or wagon.

If ``ShapeHierarchy`` is not specified in a light, the light will attach
to the main body of the locomotive or wagon by default.

.. _features-light-conditions:

Open Rails specific lighting conditions
Expand Down
1 change: 1 addition & 0 deletions Source/Documentation/Manual/news.rst
Expand Up @@ -24,6 +24,7 @@ What's been added
- Distributed Power display, in-cab and on webpage
- Progress bar for timetable "pre-run"
- HUD extended for diesel-mechanical loco
- New features for loco/wagon lighting, including horn-activated flashing ditch lights



Expand Down
28 changes: 28 additions & 0 deletions Source/Orts.Formats.Msts/LightCollection.cs
Expand Up @@ -338,6 +338,8 @@ public LightCondition(LightCondition condition, bool reverse)
public class Light
{
public int Index;
public int ShapeIndex = -1;
public string ShapeHierarchy;
public LightType Type;
public bool Cycle;
public float FadeIn;
Expand Down Expand Up @@ -369,12 +371,16 @@ public Light(int index, STFReader stf)
if (States.Count < count)
STFException.TraceWarning(stf, (count - States.Count).ToString() + " missing State(s)");
}),
new STFReader.TokenProcessor("shapeindex", ()=>{ ShapeIndex = stf.ReadIntBlock(null); }),
new STFReader.TokenProcessor("shapehierarchy", ()=>{ ShapeHierarchy = stf.ReadStringBlock(null).ToUpper(); }),
});
}

public Light(Light light, bool reverse)
{
Index = light.Index;
ShapeIndex = light.ShapeIndex;
ShapeHierarchy = light.ShapeHierarchy;
Type = light.Type;
Cycle = light.Cycle;
FadeIn = light.FadeIn;
Expand All @@ -396,6 +402,11 @@ public class LightCollection
{
public List<Light> Lights = new List<Light>();

// Array of bools, one per type of condition in the same order as presented in the 'LightCondition' class
// A 'true' indicates all lights in this set ignore the corresponding condition, so we don't need to waste time thinking about it
// Remember to expand this if more conditions are added!
public bool[] IgnoredConditions = new bool[15];

public LightCollection(STFReader stf)
{
stf.MustMatch("(");
Expand All @@ -410,6 +421,23 @@ public LightCollection(STFReader stf)
foreach (var light in Lights.ToArray())
if (light.Type == LightType.Cone)
Lights.Add(new Light(light, true));

// Determine which, if any, conditions are ignored by all conditions of all lights
IgnoredConditions[0] = Lights.All(light => light.Conditions.All(cond => cond.Headlight == LightHeadlightCondition.Ignore));
IgnoredConditions[1] = Lights.All(light => light.Conditions.All(cond => cond.Unit == LightUnitCondition.Ignore));
IgnoredConditions[2] = Lights.All(light => light.Conditions.All(cond => cond.Penalty == LightPenaltyCondition.Ignore));
IgnoredConditions[3] = Lights.All(light => light.Conditions.All(cond => cond.Control == LightControlCondition.Ignore));
IgnoredConditions[4] = Lights.All(light => light.Conditions.All(cond => cond.Service == LightServiceCondition.Ignore));
IgnoredConditions[5] = Lights.All(light => light.Conditions.All(cond => cond.TimeOfDay == LightTimeOfDayCondition.Ignore));
IgnoredConditions[6] = Lights.All(light => light.Conditions.All(cond => cond.Weather == LightWeatherCondition.Ignore));
IgnoredConditions[7] = Lights.All(light => light.Conditions.All(cond => cond.Coupling == LightCouplingCondition.Ignore));
IgnoredConditions[8] = Lights.All(light => light.Conditions.All(cond => cond.Battery == LightBatteryCondition.Ignore));
IgnoredConditions[9] = Lights.All(light => light.Conditions.All(cond => cond.Brake == LightBrakeCondition.Ignore));
IgnoredConditions[10] = Lights.All(light => light.Conditions.All(cond => cond.Reverser == LightReverserCondition.Ignore));
IgnoredConditions[11] = Lights.All(light => light.Conditions.All(cond => cond.Doors == LightDoorsCondition.Ignore));
IgnoredConditions[12] = Lights.All(light => light.Conditions.All(cond => cond.Horn == LightHornCondition.Ignore));
IgnoredConditions[13] = Lights.All(light => light.Conditions.All(cond => cond.Bell == LightBellCondition.Ignore));
IgnoredConditions[14] = Lights.All(light => light.Conditions.All(cond => cond.MU == LightMUCondition.Ignore));
}
}
}

0 comments on commit a948dbe

Please sign in to comment.