2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 /** @file logic_signal_program.cpp Implementation of the SignalProgram class functions. */
10 #include "logic_signals.h"
12 #include "viewport_func.h"
15 * Return the opposite of the given signal state.
16 * @param state The original signal state to revert.
17 * @return Green if the given argument is Red and vice versa.
19 static inline SignalState
OppositeSignalState(SignalState state
)
21 return state
== SIGNAL_STATE_RED
? SIGNAL_STATE_GREEN
: SIGNAL_STATE_RED
;
25 * The maximum number of signal programs which are evaluated in succession.
27 static const int MAX_LOGIC_SIGNAL_RECURSIONS
= 5;
30 * Default constructor, used by the save/load handler.
32 SignalProgram::SignalProgram()
38 * The constructor for creating a new signal program.
39 * @param t The tile where the logic signal for this program is located.
40 * @param tr The track where the logic signal for this program is located.
42 SignalProgram::SignalProgram(TileIndex t
, Track tr
)
47 /* Default to a priority signal: if any of the linked input
48 * signals are red, this one goes red. */
49 this->own_default_state
= SIGNAL_STATE_GREEN
;
50 this->trigger_state
= SIGNAL_STATE_RED
;
51 this->signal_op
= SIGNAL_OP_OR
;
53 this->blocked_by_train
= false;
57 * Add a new signal as input for this signal program.
58 * @param tile The tile of the signal to be linked.
59 * @param track The track of the signal to be linked.
61 void SignalProgram::AddLink(TileIndex tile
, Track track
, bool remove_first
)
63 SignalReference source
= GetSignalReference(tile
, track
);
65 if (!this->linked_signals
.Contains(source
)) {
66 /* Remove any existing link first, because we can only have one per signal (for now) */
67 if (remove_first
) RemoveSignalLink(tile
, track
);
69 *(this->linked_signals
.Append()) = source
;
70 _signal_link_list
[source
] = GetSignalReference(this->tile
, this->track
);
75 * Remove a linked signal from this program. The link must exist.
76 * @param tile The tile of the signal to be removed.
77 * @param track The track of the signal to be removed.
79 void SignalProgram::RemoveLink(TileIndex tile
, Track track
)
81 SignalReference key
= GetSignalReference(tile
, track
);
82 SignalReference
*value
= this->linked_signals
.Find(key
);
83 assert(value
!= this->linked_signals
.End());
84 this->linked_signals
.Erase(value
);
88 * Remove all links that this signal program has.
90 void SignalProgram::ClearAllLinks()
92 /* Delete all links from the global list too */
93 for (SignalReference
*sref
= this->linked_signals
.Begin(); sref
!= this->linked_signals
.End(); sref
++) {
94 _signal_link_list
.erase(*sref
);
97 this->linked_signals
.Clear();
101 * The number of signals linked to this signal program.
103 uint
SignalProgram::LinkCount()
105 return this->linked_signals
.Length();
109 * This function is run when one of the signals linked to this program has changed.
110 * It will (possibly) change the state of the signal, and then recursively change
111 * the state of any signal linked to it.
112 * @param depth The recursion depth, which starts at 1.
114 void SignalProgram::InputChanged(int depth
)
116 /* If this signal is blocked by a train, we can't do anything */
117 if (this->blocked_by_train
) {
121 SignalState new_state
= this->Evaluate();
123 if (new_state
!= DetermineSignalState(this->tile
, this->track
)) {
124 SetSignalStateForTrack(this->tile
, this->track
, new_state
);
125 MarkTileDirtyByTile(tile
);
127 /* Recursively update any signals that have this one as input */
128 if (depth
< MAX_LOGIC_SIGNAL_RECURSIONS
) {
129 SignalStateChanged(this->tile
, this->track
, depth
+ 1);
135 * The main evaluation function which determines the state of the signal linked
136 * to this signal program. It should be short, simple and readable.
138 SignalState
SignalProgram::Evaluate()
140 int trigger_states
= 0, not_trigger_states
= 0;
142 /* We need at least one linked signal to evaluate anything */
143 if (this->LinkCount() == 0) return this->own_default_state
;
145 /* Loop through all linked signals and count the states */
146 for (SignalReference
*sref
= this->linked_signals
.Begin(); sref
!= this->linked_signals
.End(); sref
++) {
147 TileIndex target_tile
= GetTileFromSignalReference(*sref
);
148 Track target_track
= GetTrackFromSignalReference(*sref
);
149 SignalState target_state
= DetermineSignalState(target_tile
, target_track
);
151 if (target_state
== trigger_state
) {
154 not_trigger_states
++;
158 switch (this->signal_op
) {
160 /* OR is triggered if we have at least one signal of trigger color */
161 if (trigger_states
> 0) return OppositeSignalState(this->own_default_state
);
164 /* AND is triggered if no signals were of the 'wrong' color */
165 if (not_trigger_states
== 0) return OppositeSignalState(this->own_default_state
);
168 /* NAND is triggered if we have at least one signal of the 'wrong' color */
169 if (not_trigger_states
> 0) return OppositeSignalState(this->own_default_state
);
172 /* XOR is triggered if the number of signals in trigger color is uneven */
173 if ((trigger_states
% 2) > 0) return OppositeSignalState(this->own_default_state
);
177 /* Not triggered, return default color */
178 return this->own_default_state
;