Add standard library header includes to precompiled header. Fix several uses of strin...
[openttd-joker.git] / src / logic_signal_program.cpp
blob5f312729a75b6b2f0025ed599832e136c9db4d0b
1 /*
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/>.
6 */
8 /** @file logic_signal_program.cpp Implementation of the SignalProgram class functions. */
10 #include "logic_signals.h"
11 #include "rail_map.h"
12 #include "viewport_func.h"
13 #include "overlay_cmd.h"
14 #include <algorithm>
16 /**
17 * Return the opposite of the given signal state.
18 * @param state The original signal state to revert.
19 * @return Green if the given argument is Red and vice versa.
21 static inline SignalState OppositeSignalState(SignalState state)
23 return state == SIGNAL_STATE_RED ? SIGNAL_STATE_GREEN : SIGNAL_STATE_RED;
26 /**
27 * The maximum number of signal programs which are evaluated in succession.
29 static const int MAX_LOGIC_SIGNAL_RECURSIONS = 5;
31 /**
32 * Default constructor, used by the save/load handler.
34 SignalProgram::SignalProgram()
39 /**
40 * The constructor for creating a new signal program.
41 * @param t The tile where the logic signal for this program is located.
42 * @param tr The track where the logic signal for this program is located.
44 SignalProgram::SignalProgram(TileIndex t, Track tr)
46 this->tile = t;
47 this->track = tr;
49 /* Default to a priority signal: if any of the linked input
50 * signals are red, this one goes red. */
51 this->own_default_state = SIGNAL_STATE_GREEN;
52 this->trigger_state = SIGNAL_STATE_RED;
53 this->signal_op = SIGNAL_OP_OR;
55 this->blocked_by_train = false;
58 /**
59 * Add a new signal as input for this signal program.
60 * @param tile The tile of the signal to be linked.
61 * @param track The track of the signal to be linked.
63 void SignalProgram::AddLink(TileIndex tile, Track track, bool remove_first)
65 SignalReference source = GetSignalReference(tile, track);
67 if (!this->linked_signals.Contains(source)) {
68 /* Remove any existing link first, because we can only have one per signal (for now) */
69 if (remove_first) RemoveSignalLink(tile, track);
71 *(this->linked_signals.Append()) = source;
72 _signal_link_list[source] = GetSignalReference(this->tile, this->track);
75 Overlays::Instance()->RefreshLogicSignalOverlay();
78 /**
79 * Remove a linked signal from this program. The link must exist.
80 * @param tile The tile of the signal to be removed.
81 * @param track The track of the signal to be removed.
83 void SignalProgram::RemoveLink(TileIndex tile, Track track)
85 // Refresh BEFORE because we need to know what tiles to refresh before we remove the reference to them.
86 Overlays::Instance()->RefreshLogicSignalOverlay();
88 SignalReference key = GetSignalReference(tile, track);
89 SignalReference *value = this->linked_signals.Find(key);
90 assert(value != this->linked_signals.End());
91 this->linked_signals.Erase(value);
94 /**
95 * Remove all links that this signal program has.
97 void SignalProgram::ClearAllLinks()
99 // Refresh BEFORE because we need to know what tiles to refresh before we remove the reference to them.
100 Overlays::Instance()->RefreshLogicSignalOverlay();
102 /* Delete all links from the global list too */
103 for (SignalReference *sref = this->linked_signals.Begin(); sref != this->linked_signals.End(); sref++) {
104 _signal_link_list.erase(*sref);
107 this->linked_signals.Clear();
110 * Return all signal input references.
112 const std::list<SignalReference> SignalProgram::GetSignalReferences() const
114 std::list<SignalReference> reference_list;
116 for (const SignalReference* ref = this->linked_signals.Begin(); ref != this->linked_signals.End(); ++ref) {
117 assert(ref != nullptr);
118 reference_list.push_back(*ref);
121 return reference_list;
125 * The number of signals linked to this signal program.
127 uint SignalProgram::LinkCount()
129 return this->linked_signals.Length();
133 * This function is run when one of the signals linked to this program has changed.
134 * It will (possibly) change the state of the signal, and then recursively change
135 * the state of any signal linked to it.
136 * @param depth The recursion depth, which starts at 1.
138 void SignalProgram::InputChanged(int depth)
140 /* If this signal is blocked by a train, we can't do anything */
141 if (this->blocked_by_train) {
142 return;
145 SignalState new_state = this->Evaluate();
147 if (new_state != DetermineSignalState(this->tile, this->track)) {
148 SetSignalStateForTrack(this->tile, this->track, new_state);
149 MarkTileDirtyByTile(tile);
151 /* Recursively update any signals that have this one as input */
152 if (depth < MAX_LOGIC_SIGNAL_RECURSIONS) {
153 SignalStateChanged(this->tile, this->track, depth + 1);
159 * The main evaluation function which determines the state of the signal linked
160 * to this signal program. It should be short, simple and readable.
162 SignalState SignalProgram::Evaluate()
164 int trigger_states = 0, not_trigger_states = 0;
166 /* We need at least one linked signal to evaluate anything */
167 if (this->LinkCount() == 0) return this->own_default_state;
169 /* Loop through all linked signals and count the states */
170 for (SignalReference *sref = this->linked_signals.Begin(); sref != this->linked_signals.End(); sref++) {
171 TileIndex target_tile = GetTileFromSignalReference(*sref);
172 Track target_track = GetTrackFromSignalReference(*sref);
173 SignalState target_state = DetermineSignalState(target_tile, target_track);
175 if (target_state == trigger_state) {
176 trigger_states++;
177 } else {
178 not_trigger_states++;
182 switch (this->signal_op) {
183 case SIGNAL_OP_OR:
184 /* OR is triggered if we have at least one signal of trigger color */
185 if (trigger_states > 0) return OppositeSignalState(this->own_default_state);
186 break;
187 case SIGNAL_OP_AND:
188 /* AND is triggered if no signals were of the 'wrong' color */
189 if (not_trigger_states == 0) return OppositeSignalState(this->own_default_state);
190 break;
191 case SIGNAL_OP_NAND:
192 /* NAND is triggered if we have at least one signal of the 'wrong' color */
193 if (not_trigger_states > 0) return OppositeSignalState(this->own_default_state);
194 break;
195 case SIGNAL_OP_XOR:
196 /* XOR is triggered if the number of signals in trigger color is uneven */
197 if ((trigger_states % 2) > 0) return OppositeSignalState(this->own_default_state);
198 break;
201 /* Not triggered, return default color */
202 return this->own_default_state;