class SmartSignal isclass Signal
{
//these are the basic signal types
//01 - n/a
//02 - n/a
//03 - absolute signals with slow approach aspects as the highest aspect
//04 - absolute signal
//05 - permissive signal
//06 - double head diverging signal
//07 - double head converging signal (top head always red)
//08 - triple head speed interlocking signal
//081 - triple head speed interlocking signal with approach aspects as highest
//these are the states that default trainz signalling can understand.
//0 EX_STOP
//1 EX_STOP_THEN_CONTINUE
//4 EX_CAUTION
//7 EX_ADVANCE_CAUTION
//8 EX_PROCEED
//2 EX_CAUTION_LEFT
//3 EX_CAUTION_RIGHT
//9 EX_SLOW
//10 EX_MEDIUM
//11 EX_ADVANCE_CAUTION_LEFT
//12 EX_ADVANCE_CAUTION_RIGHT
//5 EX_PROCEED_LEFT
//6 EX_PROCEED_RIGHT
// so I will take thse and make them more understandable for these scripts, which use the aspect slots but change the names.
// Limited Speed........45mph
// Medium Speed.........30mph
// Slow Speed...........15mph
// Restricted Speed.....15mph
// not used as a visible state
public define int SS_DARK = 25; //!< to turn off lens
// shared states
public define int SS_STOP = 0; //!< Stop incidcation.
public define int SS_STOP_THEN_PROCEED = 1; //!< Permissive Stop indication.
public define int SS_APPROACH = 4; //!< The next signal is red.
public define int SS_ADVANCE_APPROACH = 7; //!< The next signal is yellow.
public define int SS_PROCEED = 8; //!< Proceed.
// 06D and 06
public define int SS_APPROACH_LIMITED = 12; //!< Approach next signal not exceeding Limited Speed.
public define int SS_APPROACH_MEDIUM = 11; //!< Approach next signal not exceeding Medium Speed.
// 06 and 08
public define int SS_LIMITED_CLEAR = 6; //!< Limited speed through turnouts, crossovers, sidings, and power operated switches; then proceed at maximum speed permitted.
public define int SS_MEDIUM_CLEAR = 5; //!< Medium speed through turnouts, crossovers, sidings, and power operated switches; then proceed at maximum speed permitted.
public define int SS_LIMITED_APPROACH = 2; //!< Limited speed through turnouts, crossovers, sidings, and power operated switches; then proceed prepared to stop at next signal.
public define int SS_MEDIUM_APPROACH = 3; //!< Medium speed through turnouts, crossovers, sidings, and power operated switches; then proceed prepared to stop at next signal. Diverging Approach.
// special states
public define int SS_RESTRICTING = 9; //!< Proceed at restricting speed.
public define int SS_SLOW = 10; //!< Proceed at Slow speed.
//B&O 03 CPLs
public define int SS_SLOW_APPROACH = 9; //!<
public define int SS_SLOW_APPROACH_SLOW = 7;
// extended states not understood by default trainz signals... be careful with these.
// 08 signals only.
public define int SS_MED_APPROACH_MEDIUM = 13; //!< Medium speed through turnouts, crossovers, sidings, and power operated switches; then proceed approaching next signal not exceeding Medium Speed.
public define int SS_MED_APPROACH_SLOW = 14; //!< Medium speed through turnouts, crossovers, sidings, and power operated switches; then proceed approaching next signal not exceeding Slow Speed.
public define int SS_APPROACH_RESTRICTING = 15; //!< 06d heads only when placed before 06 or 08
public define int SS_APPROACH_SLOW = 16; //08 B&O
//head angle snap amount
public define int SS_ANGLE_SNAP = 5; //degrees
Display More
public string DescState(int State, StringTable m_textStrings)
{
string desc;
switch (State)
{
case SS_STOP:
desc = m_textStrings.GetString("signal_stop");
break;
case SS_STOP_THEN_PROCEED:
desc = m_textStrings.GetString("signal_stop_proceed");
break;
case SS_LIMITED_APPROACH:
desc = m_textStrings.GetString("signal_limited_approach");
break;
case SS_MEDIUM_APPROACH:
desc = m_textStrings.GetString("signal_medium_approach");
break;
case SS_APPROACH:
desc = m_textStrings.GetString("signal_approach");
break;
case SS_MEDIUM_CLEAR:
desc = m_textStrings.GetString("signal_medium_clear");
break;
case SS_LIMITED_CLEAR:
desc = m_textStrings.GetString("signal_limited_clear");
break;
case SS_ADVANCE_APPROACH:
desc = m_textStrings.GetString("signal_advance_approach");
break;
case SS_PROCEED:
desc = m_textStrings.GetString("signal_clear");
break;
case SS_RESTRICTING:
desc = m_textStrings.GetString("signal_restricting");
break;
case SS_SLOW:
desc = m_textStrings.GetString("signal_slow_clear");
break;
case SS_APPROACH_MEDIUM:
desc = m_textStrings.GetString("signal_approach_medium");
break;
case SS_APPROACH_LIMITED:
desc = m_textStrings.GetString("signal_approach_limited");
break;
case SS_MED_APPROACH_MEDIUM:
desc = m_textStrings.GetString("signal_med_approach_med");
break;
case SS_MED_APPROACH_SLOW:
desc = m_textStrings.GetString("signal_med_approach_slow");
break;
case SS_APPROACH_RESTRICTING:
desc = m_textStrings.GetString("signal_approach_restr");
break;
case SS_APPROACH_SLOW:
desc = m_textStrings.GetString("signal_approach_slow");
break;
default:
desc = m_textStrings.GetString("signal_error");
}
return desc;
}
};
Display More
- sets the rotation of the signal head
- MeshObject a signal is a signal asset
- plovák headDeflection is the deflection angle in degrees
static class SmartSignalCommon
{
public void SetHeadDeflection(MeshObject signal, float headDeflection)
{
float angle = headDeflection * (Math.PI/180);
if(signal.HasMesh("head_1")) {signal.SetMeshOrientation("head_1", 0, 0, angle);}
if(signal.HasMesh("head_2")) {signal.SetMeshOrientation("head_2", 0, 0, angle);}
if(signal.HasMesh("head_3")) {signal.SetMeshOrientation("head_3", 0, 0, angle);}
}
- sets the letters of the signal tables
- p_name: is a mesh board
- p_color: it must be an alphabetical set of textures listing 1-26, 27-36 because the numbers '0' are empty
- p_value: is a letter (only one letter is accepted and the string will be truncated to one letter)
public void SetLetters(MeshObject signal, string p_name, Asset p_color , string p_value)
{
Str.ToUpper(p_value);
if (p_value == "N") signal.SetFXTextureReplacement(p_name,p_color,14);
else if (p_value == "S") signal.SetFXTextureReplacement(p_name,p_color,19);
else if (p_value == "E") signal.SetFXTextureReplacement(p_name,p_color,5);
else if (p_value == "W") signal.SetFXTextureReplacement(p_name,p_color,23);
else if (p_value == "A") signal.SetFXTextureReplacement(p_name,p_color,1);
else if (p_value == "B") signal.SetFXTextureReplacement(p_name,p_color,2);
else if (p_value == "C") signal.SetFXTextureReplacement(p_name,p_color,3);
else if (p_value == "D") signal.SetFXTextureReplacement(p_name,p_color,4);
else if (p_value == "F") signal.SetFXTextureReplacement(p_name,p_color,6);
else if (p_value == "G") signal.SetFXTextureReplacement(p_name,p_color,7);
else if (p_value == "H") signal.SetFXTextureReplacement(p_name,p_color,8);
else if (p_value == "I") signal.SetFXTextureReplacement(p_name,p_color,9);
else if (p_value == "J") signal.SetFXTextureReplacement(p_name,p_color,10);
else if (p_value == "K") signal.SetFXTextureReplacement(p_name,p_color,11);
else if (p_value == "L") signal.SetFXTextureReplacement(p_name,p_color,12);
else if (p_value == "M") signal.SetFXTextureReplacement(p_name,p_color,13);
else if (p_value == "O") signal.SetFXTextureReplacement(p_name,p_color,15);
else if (p_value == "P") signal.SetFXTextureReplacement(p_name,p_color,16);
else if (p_value == "Q") signal.SetFXTextureReplacement(p_name,p_color,17);
else if (p_value == "R") signal.SetFXTextureReplacement(p_name,p_color,18);
else if (p_value == "T") signal.SetFXTextureReplacement(p_name,p_color,20);
else if (p_value == "U") signal.SetFXTextureReplacement(p_name,p_color,21);
else if (p_value == "V") signal.SetFXTextureReplacement(p_name,p_color,22);
else if (p_value == "X") signal.SetFXTextureReplacement(p_name,p_color,24);
else if (p_value == "Y") signal.SetFXTextureReplacement(p_name,p_color,25);
else if (p_value == "Z") signal.SetFXTextureReplacement(p_name,p_color,26);
else if (p_value == "0") signal.SetFXTextureReplacement(p_name,p_color,27);
else if (p_value == "1") signal.SetFXTextureReplacement(p_name,p_color,28);
else if (p_value == "2") signal.SetFXTextureReplacement(p_name,p_color,29);
else if (p_value == "3") signal.SetFXTextureReplacement(p_name,p_color,30);
else if (p_value == "4") signal.SetFXTextureReplacement(p_name,p_color,31);
else if (p_value == "5") signal.SetFXTextureReplacement(p_name,p_color,32);
else if (p_value == "6") signal.SetFXTextureReplacement(p_name,p_color,33);
else if (p_value == "7") signal.SetFXTextureReplacement(p_name,p_color,34);
else if (p_value == "8") signal.SetFXTextureReplacement(p_name,p_color,35);
else if (p_value == "9") signal.SetFXTextureReplacement(p_name,p_color,36);
else signal.SetFXTextureReplacement(p_name,p_color,0);
//Interface.Log(p_name + ": " + p_value);
}
Display More
Basic property types for all SmartSignals in Surveyor
public string SSPropertyTypes(string pID)
{
string result;
if (pID[,6] == "sigNum") result = "string,1,5";
else if (pID[,8] == "powerNum") result = "int,0,9999,1";
else if (pID[,9] == "plateType") result = "list";
else result = "link";
return result;
}
Creates an HTML slider
string GetSliderHTML(string name, int min, int max)
{
// Generate a html slider using a horizontal scroll bar
string html;
html = html + "<a href='live://property/" + name + "'>";
html = html + " <trainz-object id='" + name + "' style=slider horizontal theme=default-slider width=200 height=20 min=" + min + " max=" + max + " page-size=1 draw-line=1 draw-marks=1>";
html = html + " </trainz-object>";
html = html + "</a>";
return html;
}
Basic user interface for all SmartSignals in Surveyor
public string SurveyorHTML(StringTable m_textStrings, Soup properties)
{
Asset SSAsset = World.FindAsset(properties.GetNamedTagAsKUID("SSkuid"));
HTMLBuffer Buffer = HTMLBufferStatic.Construct();
Buffer.Clear();
Buffer.Print("<html><body><font size=3><br>");
string type = properties.GetNamedTag("SignalType");
//--------------------------------signal head angle-----------------------------------
if (properties.GetNamedTagAsBool("showHeadAngleUI",false))
{
Buffer.Print("<p>" + m_textStrings.GetString("cfg_head_angle") + GetSliderHTML("headAngle", -(90 / SmartSignal.SS_ANGLE_SNAP), 90 / SmartSignal.SS_ANGLE_SNAP) + "</p>");
}
//--------------------------------path interface-----------------------------------
if (properties.GetNamedTagAsBool("showPathUI",false))
{
string pathStraight = properties.GetNamedTag("pathStraight");
string pathImg;
KUID kuidImgLeft = SSAsset.LookupKUIDTable("imgLeft");
KUID kuidImgRight = SSAsset.LookupKUIDTable("imgRight");
KUID kuidImgFwd = SSAsset.LookupKUIDTable("imgFwd");
int i;
Buffer.Print("<p>" + m_textStrings.GetString("cfg_define_path") + ": <br>");
for (i = 0; i < pathStraight.size(); i++)
{
if (i > 0) Buffer.Print(" | ");
//determine the icon to display
if (pathStraight[i,i + 1] == "L")
{
pathImg = kuidImgLeft.GetHTMLString();
}
else if (pathStraight[i,i + 1] == "R")
{
pathImg = kuidImgRight.GetHTMLString();
}
else if (pathStraight[i,i + 1] == "F")
{
pathImg = kuidImgFwd.GetHTMLString();
}
//display link
Buffer.Print("<a href=live://property/path" + i + ">");
Buffer.Print("<img kuid='" + pathImg + "' width=32 height=32>");
Buffer.Print("</a>");
}
Buffer.Print("</p><br>");
Buffer.Print("<p>" + m_textStrings.GetString("cfg_edit") + ": <a href=live://property/clear>" + m_textStrings.GetString("cfg_clear") + "</a>");
Buffer.Print(" or <a href=live://property/remove>" + m_textStrings.GetString("cfg_remove") + "</a>");
Buffer.Print(" or <a href=live://property/add>" + m_textStrings.GetString("cfg_add") + "</a>");
Buffer.Print("</p><br>");
}
Display More
Unbound control
if ((type == "06" or type == "06prr") and (properties.GetNamedTagAsBool("showPathUI",false)))
{
Buffer.Print("<p>" + HTMLWindow.CheckBox("live://property/detBond", properties.GetNamedTagAsBool("unbonded",false)) + m_textStrings.GetString("cfg_unbonded") + "</p>");
}
Activate limited speed
if (properties.GetNamedTagAsBool("showPathUI",false) and (!properties.GetNamedTagAsBool("unbonded",false)) and (
type == "06"
or type == "08"
or type == "08-1"))
{
Buffer.Print("<p>" + HTMLWindow.CheckBox("live://property/limited", properties.GetNamedTagAsBool("limitedSpeed",false)) + m_textStrings.GetString("cfg_limited_speed") + "</p>");
}
Show approach or clear
if (properties.GetNamedTagAsBool("showAdvAppUI",false))
{
Buffer.Print("<p>" + HTMLWindow.CheckBox("live://property/advanceApp", properties.GetNamedTagAsBool("advanceApp",false)) + m_textStrings.GetString("cfg_advance_app") + "</p>");
}
Display restrictions
if (properties.GetNamedTagAsBool("showRestrictingUI",false))
{
Buffer.Print("<p>" + HTMLWindow.CheckBox("live://property/restricting", properties.GetNamedTagAsBool("allowRestricting",false)) + m_textStrings.GetString("cfg_restricting") + "</p>");
}
Zoom in on lighting and channel
bool approachLit = properties.GetNamedTagAsBool("approachLit",false);
Buffer.Print("<p>" + HTMLWindow.CheckBox("live://property/approachLit", approachLit) + m_textStrings.GetString("cfg_approach_lit") + "</p>");
if (approachLit)
{
int gChannel = properties.GetNamedTagAsInt("gChannel",0);
Buffer.Print("<p>" + m_textStrings.GetString("cfg_approach_lit_channel") + ": <a href=live://property/powerNum>");
if (gChannel)
{
Buffer.Print(gChannel);
}
else
{
Buffer.Print(m_textStrings.GetString("cfg_none"));
}
Buffer.Print("</a></p>");
}
Display More
Show clearly when idle
if( (type == "03")
or (type == "04")
or (type == "05")
or (type == "06")
or (type == "06D"))
{
Buffer.Print("<p>" + HTMLWindow.CheckBox("live://property/idleClear", properties.GetNamedTagAsBool("idleClear",false)) + m_textStrings.GetString("cfg_idle_clear") + "</p>");
}
Stop type
if (properties.GetNamedTagAsBool("showPRRstop",false))
{
Buffer.Print("<p>" + HTMLWindow.CheckBox("live://property/stopType", properties.GetNamedTagAsBool("stopType",false)) + m_textStrings.GetString("cfg_prr_stop_type") + "</p>");
}
The train orders a light
if (properties.GetNamedTagAsBool("showOrderLightUI",false))
{
bool showLamp = properties.GetNamedTagAsBool("showOrderLight",false);
bool onLeft = properties.GetNamedTagAsBool("ordersLightOnLeft",false);
Buffer.Print("<p>" + HTMLWindow.CheckBox("live://property/orderslight", showLamp)
+ m_textStrings.GetString("cfg_show_orders_light"));
if (showLamp)
{
Buffer.Print(": <a href=live://property/olside>");
if(onLeft)
{
Buffer.Print(m_textStrings.GetString("cfg_left"));
} else {
Buffer.Print(m_textStrings.GetString("cfg_right"));
}
Buffer.Print("</a></p>");
}
}
Display More
Setting the tables and markers for the signal
if (properties.GetNamedTagAsBool("showAbsUI",false))
{
Buffer.Print("<p>" + HTMLWindow.CheckBox("live://property/aplate", properties.GetNamedTagAsBool("aplate",false)) + m_textStrings.GetString("cfg_abslt_plate") + "</p>");
}
if (properties.GetNamedTagAsBool("showGrdUI",false))
{
Buffer.Print("<p>" + HTMLWindow.CheckBox("live://property/gplate", properties.GetNamedTagAsBool("gplate",false)) + m_textStrings.GetString("cfg_grade_plate") + "</p>");
}
if (properties.GetNamedTagAsBool("showRplUI",false))
{
Buffer.Print("<p>" + HTMLWindow.CheckBox("live://property/rplate", properties.GetNamedTagAsBool("rplate",false)) + m_textStrings.GetString("cfg_restr_plate") + "</p>");
}
Display More
Marking
if (properties.GetNamedTagAsBool("showMarkerUI",false))
{
Buffer.Print("<p>" + HTMLWindow.CheckBox("live://property/marker", properties.GetNamedTagAsBool("marker",false)) + m_textStrings.GetString("cfg_marker") + "</p>");
}
Number plate
if (properties.GetNamedTagAsBool("showPlateUI",false))
{
int plateType = properties.GetNamedTagAsInt("plateType");
string plateDesc = properties.GetNamedTag("plateDesc");
Buffer.Print(m_textStrings.GetString("cfg_number_plate_type") + ": <a href=live://property/plateType>");
if (plateType > -1)
{
Buffer.Print(plateDesc);
}
else
{
Buffer.Print(m_textStrings.GetString("cfg_default"));
}
Buffer.Print("</a><br>");
Display More
string myNum = properties.GetNamedTag("number");
Buffer.Print(m_textStrings.GetString("cfg_signal_number") + ": <a href=live://property/sigNum>");
if (myNum != "")
{
Buffer.Print(myNum);
}
else
{
Buffer.Print(m_textStrings.GetString("cfg_none"));
}
Buffer.Print("</a><br>");
}
Display More
Termination of HTML
Buffer.Print("</font></body></html>");
return Buffer.AsString();
}
public string SSGetPropValue(StringTable m_textStrings, Soup properties, string pID)
{
string result;
if (pID[,8] == "powerNum")
{
int gChannel = properties.GetNamedTagAsInt("gChannel",0);
if(gChannel >= 0)
{
result = gChannel;
}
else
result = "";
}
if (pID[,6] == "sigNum")
{
string myNum = properties.GetNamedTag("number");
if (myNum != "")
{
result = myNum;
}
else
result = "";
}
return result;
}
Display More
Basic user interface for all SmartSignals in the controller
public string DriverHTML(StringTable m_textStrings, Soup properties)
{
HTMLBuffer Buffer = HTMLBufferStatic.Construct();
Buffer.Clear();
Buffer.Print("<html><body>");
//can we display restricting override?
if(properties.GetNamedTagAsBool("show_override",false))
{
Buffer.Print("<p>" + HTMLWindow.CheckBox("live://property/restricting", properties.GetNamedTagAsBool("restricting_override",false))
+ m_textStrings.GetString("cfg_toggle_restricting") + "</p>");
}
//can we display train order indicator
if(properties.GetNamedTagAsBool("show_orders_toggle",false))
{
Buffer.Print("<p>" + HTMLWindow.CheckBox("live://property/orders", properties.GetNamedTagAsBool("has_orders",false))
+ m_textStrings.GetString("cfg_toggle_orders") + "</p>");
}
//end html
Buffer.Print("</body></html>");
return Buffer.AsString();
}
Display More
Common logic section of signals
Block distance to the next absolute signal
public float BlockDistance(Signal start)
{
//this returns the block distance to the next absolute signal, and also the junction beyond that one for the opposing trains.
MapObject dMapObject;
GSTrackSearch DGST = start.BeginTrackSearch(true);
int cntr = 0;
float distance = 0;
Signal nextSignal = null;
while(dMapObject = DGST.SearchNext())
{
if(cast<Signal> dMapObject)
{
if(!DGST.GetFacingRelativeToSearchDirection())
{
nextSignal = (cast<Signal> dMapObject);
if (nextSignal.CanDisplayStateEx(1))
{
//Interface.Log("smartsignal.gs: <" + me.GetLocalisedName() + ">: Found intermediate signal.");
}
else
{
// If the next signal is absolute, set this block distance.
//This is the determined block length from the start signal to the next opposite facing absolute signal
distance = DGST.GetDistance();
break;
}
}
}
}
return distance;
}
Display More
- Param: start....The signal to start from
- Param: searchDirection.....Which direction to search. 'False' == behind the signal, 'True' == before the signal.
- Param: numSignalsToCheck.....Number of signal blocks to scan. 0 for any.
- Param: maxDistance.....How far on the track chart to search for a train
It checks if the block is active and returns a reference to the train if found.
The train does not need to move to activate the block.
public Train checkBlockActive(Signal start, bool searchDirection, int numSignalsToCheck, float maxDistance)
{
GSTrackSearch myGST = start.BeginTrackSearch(searchDirection);
MapObject nextMapObject;
int signalCount = 0;
Vehicle theVeh;
while(nextMapObject = myGST.SearchNext())
{
if(myGST.GetDistance() > maxDistance)
{
return null;
}
else if(cast<Signal> nextMapObject)
{
if(!myGST.GetFacingRelativeToSearchDirection())
{
if((numSignalsToCheck > 0) and (++signalCount > numSignalsToCheck))
{
return null;
}
}
}
else if(cast<Vehicle> nextMapObject)
{
theVeh = cast<Vehicle> nextMapObject;
return theVeh.GetMyTrain();
}
}
return null;
}
Display More
Find the closest approaching train for a specific signal
The train must move towards or in the direction of the signal and stop.
public Train TrainMovingTowardSignal(Signal start, bool searchDirection, int numSignalsToCheck, float maxDistance)
{
GSTrackSearch myGST = start.BeginTrackSearch(searchDirection);
MapObject nextMapObject;
Vehicle theVeh;
Train theTrain;
int signalCount;
while(nextMapObject = myGST.SearchNext())
{
if(myGST.GetDistance() > maxDistance)
{
return null;
}
else if(cast<Signal> nextMapObject)
{
if(!myGST.GetFacingRelativeToSearchDirection())
{
if((numSignalsToCheck > 0) and (++signalCount > numSignalsToCheck))
{
return null;
}
}
}
else if(cast<Vehicle> nextMapObject)
{
theVeh = cast<Vehicle> nextMapObject;
theTrain = theVeh.GetMyTrain();
if(!theTrain.TracksideIsInFront(start,(myGST.GetDistance() + 200)))
{
//train is facing away from signal
if(theTrain.GetTrainVelocity() < -0.01) { //if train is backing in my direction
return theTrain;
} else {
return null;
}
} else {
//train is facing signal
if(theTrain.GetTrainVelocity() > -0.01) { //if train is moving forward or facing my direction and stopped.
return theTrain;
} else {
return null;
}
}
}
}
return null;
}
};
Display More
Comments