The beginning of the code
C++
public Soup DetermineUpdatedState(void)
{
//Interface.Print("SSignal <" + me.GetLocalisedName() + ">: DetermineUpdatedState called!");
MapObject nextMapObject;
JunctionBase lastJunction = null; //this is just the most recent junction found in the tracksearch
JunctionBase revJunction = null; //this is the first junction that doesnt match the straight path
string pathJunction;
int pathIndex = 0;
int trainApproaching = -1; //0 = train not facing this signal. 1 = train is facing this signal.
int trainLeaving = -1; //0 = train heading to this signal from ahead. 1 = no train or moving away.
Signal nextSignal = null;
Train approachingTrain = null;
Train trainInBlock = null;
float signalDistance, maxDistance;
bool trainInPortalArea;
bool lightSignal = false;
bool activeBlock = false;
int signalState = -1;
string signalStateReason = "";
string myName = "";
if(me.GetLocalisedName() != "")
{
myName = me.GetLocalisedName() + " - ";
}
srchDst = SmartSignalCommon.BlockDistance(me);
Display More
Block Settings of Signals
Check to see if the block we signal to is active at all and if it is, light the signal, otherwise its idle.
C++
// block set-up
// check to see if the block we signal to is active at all and if it is, light the signal, otherwise its idle.
trainInBlock = SmartSignalCommon.checkBlockActive(me, false, 2, 16090);
if(trainInBlock)
{
activeBlock = true;
lightSignal = true;
}
//check to see if a train is looking at this signal (train does not have to be moving, just facing this signal)
approachingTrain = SmartSignalCommon.TrainMovingTowardSignal(me, false, 4, 16090);
if(approachingTrain and approachingTrain.GetFrontmostLocomotive())
{
trainApproaching = 1;
}
else
{
trainApproaching = 0;
}
//look for trains in the block protected by this signal and heading this way. (train must be moving toward this signal)
trainInBlock = null;
trainInBlock = SmartSignalCommon.TrainMovingTowardSignal(me, true , 0, srchDst);
if (trainInBlock)
{
trainLeaving = 0; //there is a train heading our way from up ahead.
}
else
{
trainLeaving = 1; //there is no train or it is moving away
}
Display More
Switch settings
C++
//set switch locks
if (updateLocks and ctrlJctList.size() > 0)
{
trainInBlock = null;
trainInBlock = SmartSignalCommon.TrainMovingTowardSignal(me, false, 0, 400);
lockJcts = (trainInBlock and (Math.Fabs(trainInBlock.GetTrainVelocity()) > 0.1));
SmartSignalCommon.SetJunctionLock(m_signalToken, ctrlJctList, lockJcts);
}
updateLocks = false;
Channel control
C++
//check channels
if(lightSignal and gChannel and !isActiveSignal)//send message to channel that I am active
{
isActiveSignal = true;
PostMessage(null, "SSChannelON", gChannel, 0.5);
}
if(!lightSignal and isActiveSignal) //can only post off message if it has posted on message.
{
isActiveSignal = false;
activeChannel = false;
PostMessage(null, "SSChannelOFF", gChannel, 0.1);
}
if(activeChannel) lightSignal = true;
Display More
Determine signal status
C++
//determine signal state
if (approachLit and !lightSignal) //turn signal off
{
signalState = SS_DARK;
signalStateReason = myName + m_textStrings.GetString("signal_approach_lit");
}
else if (idleRed and !activeBlock) //make signal red
{
signalState = SS_STOP;
signalStateReason = myName + m_textStrings.GetString("signal_notrain");
}
else if (activeBlock and trainApproaching == 0) //block has not been cleared
{
signalState = SS_STOP;
signalStateReason = myName + m_textStrings.GetString("signal_train_not_cleared");
}
else if (trainLeaving == 0) //block is reserved
{
signalState = SS_STOP;
signalStateReason = myName + m_textStrings.GetString("signal_reserved");
}
else //turn this signal on and perform logic
{
//GSTrackSearch our way down the track, and find out what is ahead of us.
GSTrackSearch myGST = me.BeginTrackSearch(true);
while (nextMapObject = myGST.SearchNext())
{
if (cast<Vehicle> nextMapObject and !me.GetIsRepeater())
{
//Interface.Log("JR SmartSignal 06signal.gs <" + me.GetLocalisedName() + ">: Found traincar \"" + nextMapObject.GetLocalisedName() + "\".");
// Base overlap test used here to prevent home signals from displaying confusing behaviour
// on very small portal-to-terminus layouts (e.g. Portal -> Home -> Buffers)
// and far too heavy traffic generated from the portal
//updateLocks = true;
if (myGST.GetDistance() < 100)
{
signalState = EX_STOP;
signalStateReason = myName + m_textStrings.GetString("signal_block_occupied");
SetAutopilotHintObj(nextMapObject);
RestrictingOverride = false;
break;
}
if ((overlapTC) or (me.GetIsHome() and (me.GetOverlap() != baseOverlap)) or (myGST.GetDistance() > me.GetOverlap() - HALF_A_CAR_LENGTH))
{
signalState = EX_STOP;
signalStateReason = myName + m_textStrings.GetString("signal_block_occupied");
SetAutopilotHintObj(nextMapObject);
break;
}
else
{
//Interface.Log("JR SmartSignal 06signal.gs <" + me.GetLocalisedName() + ">: ... traincar is entirely within overlap, and is thus still in the block in rear.");
RestrictingOverride = false;
}
}
else if (cast<Junction> nextMapObject and !me.GetIsRepeater())
{
//Interface.Print("SSignal: <" + me.GetLocalisedName() + ">: Found junction \"" + nextMapObject.GetLocalisedName() + "\".");
//Interface.Log("SSignal 06signal.gs <" + me.GetLocalisedName() + ">: Found junction \"" + nextMapObject.GetLocalisedName() + "\".");
if(!lastJunction)
{
SetAutopilotJunction(cast<Junction> nextMapObject);
}
lastJunction = cast<Junction> nextMapObject;
if(trainApproaching == -1)
{
approachingTrain = SmartSignalCommon.TrainMovingTowardSignal(me, false, 5, 16090); // 5 signals or 10 miles, whichever is closer
if(approachingTrain and approachingTrain.GetFrontmostLocomotive())
{
trainApproaching = 1;
}
else
{
trainApproaching = 0;
}
}
//
// Checking the straight path
//
if (pathIndex < pathStraight.size() and !revJunction)
{
ctrlJctList[pathIndex] = lastJunction; //put this junction in our control list
switch (lastJunction.GetDirection())
{
case Junction.DIRECTION_LEFT:
pathJunction = "L";
break;
case Junction.DIRECTION_RIGHT:
pathJunction = "R";
break;
default:
pathJunction = "F";
}
if (pathJunction != pathStraight[pathIndex,pathIndex + 1])
{
revJunction = lastJunction; //mark our first reverse junction found
}
pathIndex = pathIndex + 1;
}
}
else if (cast<Signal> nextMapObject)
{
//Interface.Log("JR SmartSignal 06signal.gs <" + me.GetLocalisedName() + ">: Found signal \"" + nextMapObject.GetLocalisedName() + "\" ...");
if (myGST.GetFacingRelativeToSearchDirection())
{
//Interface.Log("JR SmartSignal 06signal.gs <" + me.GetLocalisedName() + ">: ... and it's facing the correct way :)");
if ((cast<Signal> nextMapObject).GetIsRepeater())
{
// it's a repeater - ignore it.
// It is only copying it's state from the signal in advance.
// It is that signal we need to be looking at.
}
else if (me.GetIsRepeater())
{
// I am a repeater - copy my state from the signal in advance.
signalState = (cast<Signal> nextMapObject).GetSignalStateEx();
signalStateReason = myName + m_textStrings.GetString("signal_repeater");
break;
}
else if (!me.CanDisplayStateEx(SS_STOP_THEN_PROCEED) and (cast<Signal> nextMapObject).CanDisplayStateEx(SS_STOP_THEN_PROCEED) and trainLeaving == 0)
{
// If I am an absolute signal, and the next signal is not an absolute, and there is a train heading this direction.
// It's a permissive intermediate signal and there is a train heading this way after it.
signalState = EX_STOP;
signalStateReason = myName + m_textStrings.GetString("signal_conflicting");
RestrictingOverride = false;
if (junctionBeyond)
{
SetAutopilotJunction(junctionBeyond);
}
else if (lastJunction)
{
SetAutopilotHintObj(lastJunction.GetMapObject());
}
break;
}
else if (!me.CanDisplayStateEx(SS_STOP_THEN_PROCEED) and !(cast<Signal> nextMapObject).CanDisplayStateEx(SS_STOP_THEN_PROCEED) and trainLeaving == 0)
{
// If I am an absolute signal, and the next signal is absolute, and there is a train heading this direction.
signalState = EX_STOP;
signalStateReason = myName + m_textStrings.GetString("signal_conflicting");
RestrictingOverride = false;
if (junctionBeyond)
{
SetAutopilotJunction(junctionBeyond);
}
else if (lastJunction)
{
SetAutopilotHintObj(lastJunction.GetMapObject());
}
break;
}
else if ((cast<Signal> nextMapObject).GetIsDistant())
{
// it's a distant
if (!nextSignal and myGST.GetDistance() > me.GetOverlap() + HALF_A_CAR_LENGTH)
{
// it's the first distant we've seen, and it's far enough away from the start of the search to want to increment states
// remember this one, as our aspect will be one greater than this, provided we can clear to the next home.
nextSignal = cast<Signal> nextMapObject;
}
}
else
{
if (!nextSignal)
{
// we haven't seen a distant, so our aspect will be based off this home or combined signal
nextSignal = cast<Signal> nextMapObject;
// if we are a home, the distance to this signal (minus a bit of fudge distance) is our overlap
if (me.GetIsHome())
{
overlap = myGST.GetDistance() - HALF_A_CAR_LENGTH;
}
}
else
{
// if we are a home and our next signal is a distant (thus more accurately, we are a starter), set our default overlap
if (me.GetIsHome())
{
overlap = baseOverlap;
}
}
signalDistance = myGST.GetDistance();
Vehicle theVeh = null;
theVeh = CheckForTrainInOverlap(myGST, nextSignal.GetOverlap());
if (theVeh)
{
signalState = EX_STOP;
signalStateReason = myName + m_textStrings.GetString("signal_overlap");
RestrictingOverride = false;
SetAutopilotHintObj(cast<MapObject> theVeh);
}
else if (theVeh = CheckForOncomingTrain(myGST))
{
signalState = EX_STOP;
signalStateReason = myName + m_textStrings.GetString("signal_reserved");
RestrictingOverride = false;
SetAutopilotHintObj(theVeh);
}
else if(signalDistance < me.GetOverlap() + HALF_A_CAR_LENGTH)
{
// signals too close to each other for the AI to stop safely - don't advance state.
signalStateReason = myName + m_textStrings.GetString("signal_tooclose");
switch (nextSignal.GetSignalStateEx())
{
case SS_STOP:
case SS_STOP_THEN_PROCEED:
case SS_DARK:
case SS_APPROACH:
case SS_APPROACH_LIMITED:
case SS_APPROACH_MEDIUM:
case SS_ADVANCE_APPROACH:
case SS_PROCEED:
case SS_LIMITED_CLEAR:
case SS_LIMITED_APPROACH:
case SS_MEDIUM_APPROACH:
case SS_MEDIUM_CLEAR:
case SS_SLOW:
case SS_RESTRICTING:
case SS_APPROACH_RESTRICTING :
signalState = nextSignal.GetSignalStateEx();
break;
default:
signalState = SS_RESTRICTING;
signalStateReason = myName + m_textStrings.GetString("signal_tooclose_unkn");
Interface.Log("SSignal 06signal.gs <" + me.GetLocalisedName() + ">: Signal ahead is in unknown state of " + nextSignal.GetSignalStateEx());
}
}
else
{
switch (nextSignal.GetSignalStateEx())
{
case SS_STOP:
if(doubleBlocking) {
signalState = SS_RESTRICTING;
break;
}
case SS_DARK:
case SS_STOP_THEN_PROCEED:
signalState = SS_APPROACH;
break;
case SS_APPROACH_RESTRICTING :
case SS_APPROACH :
case SS_APPROACH_LIMITED:
case SS_APPROACH_MEDIUM:
if(advanceApp)
{
signalState = SS_ADVANCE_APPROACH;
} else {
signalState = SS_PROCEED;
}
break;
case SS_ADVANCE_APPROACH:
case SS_PROCEED:
signalState = SS_PROCEED;
break;
case SS_LIMITED_CLEAR:
case SS_LIMITED_APPROACH:
signalState = SS_APPROACH_LIMITED;
break;
case SS_MEDIUM_APPROACH:
case SS_MEDIUM_CLEAR:
signalState = SS_APPROACH_MEDIUM;
break;
case SS_SLOW:
case SS_RESTRICTING:
signalState = SS_APPROACH_RESTRICTING;
break;
default:
signalState = SS_RESTRICTING;
signalStateReason = myName + m_textStrings.GetString("signal_unknown");
Interface.Log("SSignal 06signal.gs <" + me.GetLocalisedName() + ">: Signal ahead is in unknown state of " + nextSignal.GetSignalStateEx());
}
}
//updateLocks = true; //update locks after clearing a path
break;
}
}
else
{
//Interface.Log("SSignal 06signal.gs <" + me.GetLocalisedName() + ">: ... but it's facing the wrong way. This means we're on bi-di track...");
}
}
else if (cast<Trackside> nextMapObject)
{
// some other trackside object.
//Interface.Log("JR SmartSignal 06signal.gs <" + me.GetLocalisedName() + ">: Found trackside scenery item.");
if ((cast<Trackside> nextMapObject).GetIsSearchLimit())
{
// oh lookee here, it's a direction marker - does it affect signalling?
if (nextMapObject.GetAsset().GetConfigSoup().GetNamedSoup("extensions").GetNamedTagAsBool("30501-search-limit-affects-signaling"))
{
if (!myGST.GetFacingRelativeToSearchDirection())
{
// facing against us
signalState = SS_RESTRICTING;
signalStateReason = myName + m_textStrings.GetString("signal_direction_mrkr");
if (lastJunction)
{
SetAutopilotHintObj(lastJunction.GetMapObject());
}
break;
}
}
}
}
else if(cast<SceneryWithTrack> nextMapObject)
{
// some kind of crossing / turntable / buildable (i.e. industry).
// Interface.Log("SSignal 06signal.gs <" + me.GetLocalisedName() + ">: Found a Scenery object with attached track.");
if((cast<SceneryWithTrack> nextMapObject).GetAttachedJunctions())
{
//a fixed track junction
//Interface.Log("JR SmartSignal 06signal.gs <" + me.GetLocalisedName() + ">: Found a Scenery object with attached track junctions.");
JunctionBase[] nodes = new JunctionBase[0];
nodes = (cast<SceneryWithTrack> nextMapObject).GetAttachedJunctions();
if(nodes.size())
{
//Interface.Print("SSignal: <" + me.GetLocalisedName() + ">: Found fixed junction \"" + nextMapObject.GetLocalisedName() + "\".");
//Interface.Print("Smartsignals:> Last junction: " + (nodes.size() - 1) );
if(!lastJunction)
{
SetAutopilotJunction(nodes[nodes.size() - 1]);
}
lastFTJunction = nodes[nodes.size() - 1];
lastJunction = lastFTJunction;
if(trainApproaching == -1)
{
approachingTrain = SmartSignalCommon.TrainMovingTowardSignal(me, false, 5, 16090); // 5 signals or 10 miles, whichever is closer
if(approachingTrain and approachingTrain.GetFrontmostLocomotive())
{
trainApproaching = 1;
}
else
{
trainApproaching = 0;
}
}
//
// Checking the straight path
//
if (pathIndex < pathStraight.size() and !revJunction)
{
ctrlJctList[pathIndex] = lastJunction; //put this junction in our control list
switch (lastJunction.GetDirection())
{
case Junction.DIRECTION_LEFT:
pathJunction = "L";
break;
case Junction.DIRECTION_RIGHT:
pathJunction = "R";
break;
default:
pathJunction = "F";
}
if (pathJunction != pathStraight[pathIndex,pathIndex + 1])
{
revJunction = nodes[nodes.size() - 1];
//Interface.Print("Smartsignals:> First junction: " + pathJunction );
}
pathIndex = pathIndex + 1;
}
}
}
if(cast<BasePortal> nextMapObject)
{
// it's a portal.
// BasePortal thePortal = cast<BasePortal> nextMapObject;
// Interface.Log("SSignal 06signal.gs <" + me.GetLocalisedName() + ">: Found a Portal");
// Search to end of track to prove no additional trains are lurking.
trainInPortalArea = false;
maxDistance = myGST.GetDistance() + PORTAL_LENGTH + HALF_A_CAR_LENGTH;
while(nextMapObject = myGST.SearchNext()) {
if(cast<Vehicle> nextMapObject and myGST.GetDistance() < maxDistance) {
signalState = EX_STOP;
signalStateReason = myName + m_textStrings.GetString("signal_block_occupied");
SetAutopilotHintObj(nextMapObject);
break;
}
}
if(signalState == -1) {
// assume portal is willing to accept a train if there is no evidence of traincars nearby.
signalStateReason = myName + m_textStrings.GetString("signal_portal");
if(lastJunction) {
if(trainApproaching)
{
signalState = EX_PROCEED;
//
// Checking the straight path
//
if (pathIndex < pathStraight.size() and !revJunction)
{
ctrlJctList[pathIndex] = lastJunction; //put this junction in our control list
switch (lastJunction.GetDirection())
{
case Junction.DIRECTION_LEFT:
pathJunction = "L";
break;
case Junction.DIRECTION_RIGHT:
pathJunction = "R";
break;
default:
pathJunction = "F";
}
if (pathJunction != pathStraight[pathIndex,pathIndex + 1])
{
revJunction = lastJunction;
}
pathIndex = pathIndex + 1;
}
} else {
signalState = EX_STOP;
signalStateReason = myName + m_textStrings.GetString("signal_portal_blocked");
}
} else {
if(!trainApproaching) {
if(idleRed) signalState = SS_STOP;
else signalState = SS_PROCEED;
signalStateReason = myName + m_textStrings.GetString("signal_notrain");
} else {
signalState = EX_PROCEED;
}
}
}
break;
}
else
{
// something other than a portal - ignore
// Interface.Log("SSignal 06signal.gs <" + me.GetLocalisedName() + ">: Scenery with track is not a Portal.");
}
}
else
{
Interface.Log("SSignal 06signal.gs <" + me.GetLocalisedName() + ">: Found some other type of object I'm not sure about.");
}
}
if (signalState == -1)
{
//Interface.Log("SSignal 06signal.gs <" + me.GetLocalisedName() + ">: Ran out of things in the search.");
signalState = EX_STOP;
if (lastJunction)
{
SetAutopilotHintObj(lastJunction.GetMapObject());
signalStateReason = myName + m_textStrings.GetString("signal_closed_junction");
}
else
{
signalStateReason = myName + m_textStrings.GetString("signal_line_terminated");
}
}
//
// With a repeater we will not change anything so if this is not a repeater then
//
if (!me.GetIsRepeater())
{
//
// A first junction means a non-match with the straight path
// So we have to change the signalstate according to this junction direction
//
if (revJunction)
{
if(unbonded)
{
signalState = SS_RESTRICTING;
}
else
{
switch (revJunction.GetDirection())
{
case Junction.DIRECTION_LEFT: //junction is switched
case Junction.DIRECTION_RIGHT:
switch (signalState)
{
case SS_STOP:
break;
case SS_APPROACH:
if(limSpd)
signalState = SS_LIMITED_APPROACH;
else
signalState = SS_MEDIUM_APPROACH;
break;
case SS_ADVANCE_APPROACH:
case SS_PROCEED:
if(limSpd)
signalState = SS_LIMITED_CLEAR;
else
signalState = SS_MEDIUM_CLEAR;
break;
default:;
}
break;
default:; //Forward
}
}
}
}
// okay, so we've found what we think is the state we want.
// however, we have no idea if the signal can display it,
// so we now check this against what the signal can do
// ***we dont need this since it can display ALL of the available aspects in the script
// signalState = FindNearestDisplayableStateEx(signalState);
}
//check to see if restricting override is in effect. - Set by dispatcher
if (RestrictingOverride)
{
signalState = SS_RESTRICTING;
}
if (signalState == SS_PROCEED and !CanDisplayStateEx(SS_PROCEED))
{
signalState = SS_APPROACH;
}
//set signal reason if it has not been set
if (signalStateReason == "")
{
signalStateReason = myName + DescState(signalState, m_textStrings);
}
Soup signalStateSoup = Constructors.NewSoup();
signalStateSoup.SetNamedTag("state", signalState);
signalStateSoup.SetNamedTag("reason", signalStateReason);
return signalStateSoup;
}
Display More
Comments