Trainz maintains a graph of connected track which is annotated with relevant objects such as Junctions, Signals, SceneryWithTrack, etc. The smallest unit of track on the graph is the TrackGraphLine which is exposed to script as Track.gs. The TrackSearch process allows code to navigate through the TrackGraph, given a starting position and a direction. The search process is represented by a cursor - a single point - which is moved along the track. Whenever an annotation is reached, the relevant object is returned. When a branch in the track is reached, a decision is made as to which direction to proceed in, and the search continues. The search terminates once the end of track is reached, or once a preset limit (distance travelled, number of objects returned, etc.) is reached.
A TrackSearch operation is performed from TrainzScript using the GSTrackSearch.gs helper object. This object cannot be instantiated directly, but is instead returned by accessor functions on objects which have a relationship with the TrackGraph.
public native GSTrackSearch TrackSide.BeginTrackSearch(bool direction); public native GSTrackSearch Track.BeginTrackSearch(bool direction); public native GSTrackSearch GSTrackSearch.CloneSearch(void);
Once you have a GSTrackSearch object, you may call the SearchNextObject member function to find the first relevant object. The function will return 'true' if an object was found, or 'false' if the search terminated as described above.
public native bool SearchNextObject(void);
After an object has been found, the accessor members of the GSTrackSearch object can be used to query specific details. No assumptions should be made about the object type; attempt a cast to the type(s) required by your script, and handle unknown types by continuing to the next object. The exact result-set returned by a TrackSearch may vary between Trainz versions, even on an identical map and session.
public native object GSTrackSearch.GetObject(void); public native float GSTrackSearch.GetDistance(void); public native Track GSTrackSearch.GetTrack(void); public native bool GSTrackSearch.GetFacingRelativeToSearchDirection(void);
A TrackSearch may be registered with a particular observer object for tracking purposes. As each result is returned from a registered search, an internal link is formed between between the result object and the search observer. Whenever a state change occurs on an object which may invalidate a prior search result, any linked observers are notified of the change. The notification is rapid but not immediate.
Change notifications are not precise - they do not specify which object changed, and may in fact indicate that multiple objects have changed. The correct course of action to take upon receiving a change notification is to refresh any searched information which needs to be kept up-to-date.
The following conditions will trigger change notifications:
Trackside object: * moved Junction lever: * toggled * permit acquired/released Signal: * state changed TrackGraphLine: * TrackStretch added/removed * Trackside object added/removed All observed objects: * call from script * object deleted All observers: * script call to prompt update
This is not a complete list at present. More will be added.
The default behavior of a TrackSearch is to treat the track graph as a line; wherever a branch occurs, the TrackSearch will follow the track in the "physcially correct" direction, similar to a Train. When enumerating the graph as a whole, this behavior may not be appropriate. The solution to this is to perform additional searches down each non-taken branch. The following factors need to be considered while approaching this problem:
- At the current time, Track may branch at a Junction, or at a SceneryWithTrack object such as a turntable. While no other types of branch are currently planned, it is conceivable that future Trainz versions may have additional branch mechanisms. It is not possible to detect these 'other branch types' with the current API.
- As branched track may combine back into the main track in varied ways, and track may loop, care should be taken not to redo searching of a Track that has already been searched.
The following accessors provide for searching non-taken branches.
public native Track JunctionBase.GetTrackInDirection(int direction); public native int JunctionBase.GetDirectionToTrack(Track track); public native Track SceneryWithTrack.GetAttachedTracks(void); public native int Track.GetDirectionToTrack(Track other);
SceneryWithTrack objects may use a (non-localised) name string to refer to their attached Tracks. At the present time, each named track is a distinct Track object limited to that SceneryWithTrack object, however this is an incorrect implementation of the TrackGraph concept and is likely to change in the future. This means that multiple names may resolve to a single Track, and that a Track does not belong to any particular SceneryWithTrack and does not have a unique name.
With this in mind, there is no simple reverse mapping for the SceneryWithTrack.GetAttachedTrack(string) function. However, as each name specified in the SceneryWithTrack config.txt file maps to a physical expanse of track, it is possible to determine whether a specified point on the track falls within a named track section.
public native SceneryWithTrack GSTrackSearch.GetAttachedTrackParentObject(void); public native string GSTrackSearch.GetAttachedTrackName(void);