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_signals.cpp Utility functions of the Logic Signals patch. */
10 #include "logic_signals.h"
12 #include "viewport_func.h"
14 #include "window_func.h"
17 * List of all signal programs.
19 SignalProgramList _signal_program_list
;
21 * List of all links between signals.
23 SignalLinkList _signal_link_list
;
26 * Remove any nasty foul-smelling signal programs taking up precious RAM.
28 void FreeAllSignalPrograms()
30 for (SignalProgramList::iterator it
= _signal_program_list
.begin(); it
!= _signal_program_list
.end(); it
++) {
31 SignalProgram
*program
= it
->second
;
35 _signal_program_list
.clear();
36 _signal_link_list
.clear();
40 * Determine the color of signals at the given tile/track.
41 * @param tile The tile where the tested signal is located.
42 * @param track The track where the tested signal is located.
43 * @return Red if either of the two possible signals is red.
45 SignalState
DetermineSignalState(TileIndex tile
, Track track
)
47 assert(HasSignalOnTrack(tile
, track
));
49 uint signal_states
= GetSignalStates(tile
);
50 byte signal_mask_for_track
= SignalOnTrack(track
);
51 byte present_signals_on_tile
= GetPresentSignals(tile
);
53 byte present_signals_on_track
= signal_mask_for_track
& present_signals_on_tile
;
54 byte signal_states_on_track
= present_signals_on_track
& signal_states
;
56 // We return red if one of the two possibly present signals is red. Both need to be green for us to accept the tile as green.
57 return (signal_states_on_track
== present_signals_on_track
) ? SIGNAL_STATE_GREEN
: SIGNAL_STATE_RED
;
61 * Set the signal state for the given tile and track to the specified state.
62 * Both possible signals for the given tile/track are set to the given state.
63 * @param tile Tile where the changed signal is located.
64 * @param track Track where the changed signal is located.
65 * @param state The new state to set to the signals.
67 void SetSignalStateForTrack(TileIndex tile
, Track track
, SignalState state
)
69 byte signal_mask_for_track
= SignalOnTrack(track
);
70 byte present_signals_on_tile
= GetPresentSignals(tile
);
72 if (state
== SIGNAL_STATE_GREEN
) {
73 SetSignalStates(tile
, GetSignalStates(tile
) | present_signals_on_tile
);
76 SetSignalStates(tile
, GetSignalStates(tile
) & ~present_signals_on_tile
);
81 * Read a Track from a TileIndex.
83 * The author of this patch has no idea how this works or what it does. The code
84 * is copied directly from GenericPlaceSignals function in rail_gui.cpp.
86 * @param tile The tile where to read the Track from.
87 * @return The read Track
89 Track
SignalTrackFromTile(TileIndex tile
)
91 TrackBits trackbits
= TrackStatusToTrackBits(GetTileTrackStatus(tile
, TRANSPORT_RAIL
, 0));
93 if (trackbits
& TRACK_BIT_VERT
) { // N-S direction
94 trackbits
= (_tile_fract_coords
.x
<= _tile_fract_coords
.y
) ? TRACK_BIT_RIGHT
: TRACK_BIT_LEFT
;
97 if (trackbits
& TRACK_BIT_HORZ
) { // E-W direction
98 trackbits
= (_tile_fract_coords
.x
+ _tile_fract_coords
.y
<= 15) ? TRACK_BIT_UPPER
: TRACK_BIT_LOWER
;
101 return FindFirstTrack(trackbits
);
105 * Combine TileIndex and Track to a SignalReference.
106 * @param tile The tile to get a signal reference from
107 * @param track The track to get a signal reference from
108 * @return The signal reference made up of the given tile and track
110 SignalReference
GetSignalReference(TileIndex tile
, Track track
)
112 return tile
| (((uint32
) track
) << 29);
116 * Extract a TileIndex from a SignalReference.
117 * @param key The signal reference to extract a TileIndex from
118 * @return The TileIndex component of the given signal reference
120 TileIndex
GetTileFromSignalReference(SignalReference key
)
122 return GB(key
, 0, 29);
126 * Extract a Track from a SignalReference.
127 * @param key The signal reference to extract a Track from
128 * @return The Track component of the given signal reference
130 Track
GetTrackFromSignalReference(SignalReference key
)
132 return (Track
) (key
>> 29);
136 * An internal helper function used in searching for a signal program.
137 * @param tile The tile to search for
138 * @param track The track to search for
139 * @return The corresponding signal program, or NULL if not found.
141 static inline SignalProgram
* DoFindSignalProgram(TileIndex tile
, Track track
)
143 SignalProgramList::iterator it
= _signal_program_list
.find(GetSignalReference(tile
, track
));
145 if (it
!= _signal_program_list
.end()) {
153 * Find a link from a signal at the given tile/track.
154 * @param tile The tile to search for.
155 * @param track The track to search for.
156 * @return The corresponding target where the link points to, if found, otherwise NULL.
157 * @bug This code returns NULL (0) as a not-found value, but that is also valid for the first Tile/Track
159 static inline SignalReference
FindSignalLink(TileIndex tile
, Track track
)
161 SignalLinkList::iterator it
= _signal_link_list
.find(GetSignalReference(tile
, track
));
163 if (it
!= _signal_link_list
.end()) {
171 * Used to find a signal program at the given tile and track.
172 * @param tile The tile to search for.
173 * @param track The track to search for.
174 * @return The signal program if found, or NULL.
176 SignalProgram
* FindSignalProgram(TileIndex tile
, Track track
)
178 SignalProgram
*program
= DoFindSignalProgram(tile
, track
);
179 assert(program
!= NULL
);
184 * Remove a signal link at the given tile and track, if it is found.
185 * It is perfectly valid to call this function even if the said tile/track has no outgoing link.
187 * @param tile The tile to search for
188 * @param track The track to search for
190 void RemoveSignalLink(TileIndex tile
, Track track
)
192 SignalReference existing
= FindSignalLink(tile
, track
);
194 if (existing
!= NULL
) {
195 /* Remove from signal program */
196 SignalProgram
*old_prog
= FindSignalProgram(GetTileFromSignalReference(existing
), GetTrackFromSignalReference(existing
));
197 old_prog
->RemoveLink(tile
, track
);
199 /* Remove from global list */
200 _signal_link_list
.erase(GetSignalReference(tile
, track
));
202 /* Invalidate any windows which have this program open */
203 InvalidateWindowData(WC_SIGNAL_PROGRAM
, existing
);
208 * Create a new signal program at the given tile and track.
209 * Used when a new logic signal is created.
210 * @param tile The tile of the logic signal
211 * @param track The track of the logic signal
212 * @return The newly created signal program
214 SignalProgram
* CreateSignalProgram(TileIndex tile
, Track track
)
216 /* Existing program for same tile/track would be a bug */
217 assert(DoFindSignalProgram(tile
, track
) == NULL
);
219 SignalProgram
*program
= new SignalProgram(tile
, track
);
220 _signal_program_list
[GetSignalReference(tile
, track
)] = program
;
225 * Delete a signal program at the given tile and track.
226 * @param tile The tile of the logic signal
227 * @param track The track of the logic signal
229 void DeleteSignalProgram(TileIndex tile
, Track track
)
231 SignalReference key
= GetSignalReference(tile
, track
);
233 /* Delete any windows which have this program open */
234 DeleteWindowById(WC_SIGNAL_PROGRAM
, key
, false);
236 /* Remove the actual program and all links attached to it */
237 SignalProgram
*program
= FindSignalProgram(tile
, track
);
238 _signal_program_list
.erase(key
);
239 program
->ClearAllLinks();
244 * Used to create or delete signal programs at the given tile when the signal type changes.
245 * @param tile The tile where the change occurred.
246 * @param track The track where the change occurred.
247 * @param old_type The old type of the changed signal
248 * @param new_type The new type of the changed signal
250 void SignalTypeChanged(TileIndex tile
, Track track
, SignalType old_type
, SignalType new_type
)
252 if (old_type
== SIGTYPE_LOGIC
) DeleteSignalProgram(tile
, track
);
253 if (new_type
== SIGTYPE_LOGIC
) CreateSignalProgram(tile
, track
);
257 * Executed whenever signal state has changed by the main program.
258 * @param tile Tile where the change occurred
259 * @param track Track where the change occurred
260 * @param depth Recursion depth, starts at 1.
262 void SignalStateChanged(TileIndex tile
, Track track
, int depth
)
264 SignalReference link
= FindSignalLink(tile
, track
);
266 SignalProgram
*program
= FindSignalProgram(GetTileFromSignalReference(link
), GetTrackFromSignalReference(link
));
267 program
->InputChanged(depth
);