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"
15 #include "overlay_cmd.h"
18 * List of all signal programs.
20 SignalProgramList _signal_program_list
;
22 * List of all links between signals.
24 SignalLinkList _signal_link_list
;
27 * Remove any nasty foul-smelling signal programs taking up precious RAM.
29 void FreeAllSignalPrograms()
31 for (SignalProgramList::iterator it
= _signal_program_list
.begin(); it
!= _signal_program_list
.end(); it
++) {
32 SignalProgram
*program
= it
->second
;
36 _signal_program_list
.clear();
37 _signal_link_list
.clear();
41 * Determine the color of signals at the given tile/track.
42 * @param tile The tile where the tested signal is located.
43 * @param track The track where the tested signal is located.
44 * @return Red if either of the two possible signals is red.
46 SignalState
DetermineSignalState(TileIndex tile
, Track track
)
48 assert(HasSignalOnTrack(tile
, track
));
50 uint signal_states
= GetSignalStates(tile
);
51 byte signal_mask_for_track
= SignalOnTrack(track
);
52 byte present_signals_on_tile
= GetPresentSignals(tile
);
54 byte present_signals_on_track
= signal_mask_for_track
& present_signals_on_tile
;
55 byte signal_states_on_track
= present_signals_on_track
& signal_states
;
57 // 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.
58 return (signal_states_on_track
== present_signals_on_track
) ? SIGNAL_STATE_GREEN
: SIGNAL_STATE_RED
;
62 * Set the signal state for the given tile and track to the specified state.
63 * Both possible signals for the given tile/track are set to the given state.
64 * @param tile Tile where the changed signal is located.
65 * @param track Track where the changed signal is located.
66 * @param state The new state to set to the signals.
68 void SetSignalStateForTrack(TileIndex tile
, Track track
, SignalState state
)
70 byte signal_mask_for_track
= SignalOnTrack(track
);
71 byte present_signals_on_tile
= GetPresentSignals(tile
);
73 if (state
== SIGNAL_STATE_GREEN
) {
74 SetSignalStates(tile
, GetSignalStates(tile
) | present_signals_on_tile
);
77 SetSignalStates(tile
, GetSignalStates(tile
) & ~present_signals_on_tile
);
82 * Read a Track from a TileIndex.
84 * The author of this patch has no idea how this works or what it does. The code
85 * is copied directly from GenericPlaceSignals function in rail_gui.cpp.
87 * @param tile The tile where to read the Track from.
88 * @return The read Track
90 Track
SignalTrackFromTile(TileIndex tile
)
92 TrackBits trackbits
= TrackStatusToTrackBits(GetTileTrackStatus(tile
, TRANSPORT_RAIL
, 0));
94 if (trackbits
& TRACK_BIT_VERT
) { // N-S direction
95 trackbits
= (_tile_fract_coords
.x
<= _tile_fract_coords
.y
) ? TRACK_BIT_RIGHT
: TRACK_BIT_LEFT
;
98 if (trackbits
& TRACK_BIT_HORZ
) { // E-W direction
99 trackbits
= (_tile_fract_coords
.x
+ _tile_fract_coords
.y
<= 15) ? TRACK_BIT_UPPER
: TRACK_BIT_LOWER
;
102 return FindFirstTrack(trackbits
);
106 * Combine TileIndex and Track to a SignalReference.
107 * @param tile The tile to get a signal reference from
108 * @param track The track to get a signal reference from
109 * @return The signal reference made up of the given tile and track
111 SignalReference
GetSignalReference(TileIndex tile
, Track track
)
113 return tile
| (((uint32
) track
) << 29);
117 * Extract a TileIndex from a SignalReference.
118 * @param key The signal reference to extract a TileIndex from
119 * @return The TileIndex component of the given signal reference
121 TileIndex
GetTileFromSignalReference(SignalReference key
)
123 return GB(key
, 0, 29);
127 * Extract a Track from a SignalReference.
128 * @param key The signal reference to extract a Track from
129 * @return The Track component of the given signal reference
131 Track
GetTrackFromSignalReference(SignalReference key
)
133 return (Track
) (key
>> 29);
137 * An internal helper function used in searching for a signal program.
138 * @param tile The tile to search for
139 * @param track The track to search for
140 * @return The corresponding signal program, or NULL if not found.
142 static inline SignalProgram
* DoFindSignalProgram(TileIndex tile
, Track track
)
144 SignalProgramList::iterator it
= _signal_program_list
.find(GetSignalReference(tile
, track
));
146 if (it
!= _signal_program_list
.end()) {
154 * Find a link from a signal at the given tile/track.
155 * @param tile The tile to search for.
156 * @param track The track to search for.
157 * @return The corresponding target where the link points to, if found, otherwise NULL.
158 * @bug This code returns NULL (0) as a not-found value, but that is also valid for the first Tile/Track
160 static inline SignalReference
FindSignalLink(TileIndex tile
, Track track
)
162 SignalLinkList::iterator it
= _signal_link_list
.find(GetSignalReference(tile
, track
));
164 if (it
!= _signal_link_list
.end()) {
172 * Used to find a signal program at the given tile and track.
173 * @param tile The tile to search for.
174 * @param track The track to search for.
175 * @return The signal program if found, or NULL.
177 SignalProgram
* FindSignalProgram(TileIndex tile
, Track track
)
179 SignalProgram
*program
= DoFindSignalProgram(tile
, track
);
180 assert(program
!= NULL
);
185 * Remove a signal link at the given tile and track, if it is found.
186 * It is perfectly valid to call this function even if the said tile/track has no outgoing link.
188 * @param tile The tile to search for
189 * @param track The track to search for
191 void RemoveSignalLink(TileIndex tile
, Track track
)
193 SignalReference existing
= FindSignalLink(tile
, track
);
195 if (existing
!= NULL
) {
196 /* Remove from signal program */
197 SignalProgram
*old_prog
= FindSignalProgram(GetTileFromSignalReference(existing
), GetTrackFromSignalReference(existing
));
198 old_prog
->RemoveLink(tile
, track
);
200 /* Remove from global list */
201 _signal_link_list
.erase(GetSignalReference(tile
, track
));
203 /* Invalidate any windows which have this program open */
204 InvalidateWindowData(WC_SIGNAL_PROGRAM
, existing
);
209 * Create a new signal program at the given tile and track.
210 * Used when a new logic signal is created.
211 * @param tile The tile of the logic signal
212 * @param track The track of the logic signal
213 * @return The newly created signal program
215 SignalProgram
* CreateSignalProgram(TileIndex tile
, Track track
)
217 /* Existing program for same tile/track would be a bug */
218 assert(DoFindSignalProgram(tile
, track
) == NULL
);
220 SignalProgram
*program
= new SignalProgram(tile
, track
);
221 _signal_program_list
[GetSignalReference(tile
, track
)] = program
;
226 * Delete a signal program at the given tile and track.
227 * @param tile The tile of the logic signal
228 * @param track The track of the logic signal
230 void DeleteSignalProgram(TileIndex tile
, Track track
)
232 SignalReference key
= GetSignalReference(tile
, track
);
234 /* Delete any windows which have this program open */
235 DeleteWindowById(WC_SIGNAL_PROGRAM
, key
, false);
237 /* Remove the actual program and all links attached to it */
238 SignalProgram
*program
= FindSignalProgram(tile
, track
);
240 Overlays::Instance()->HandleSignalProgramDeletion(program
);
242 _signal_program_list
.erase(key
);
243 program
->ClearAllLinks();
249 * Used to create or delete signal programs at the given tile when the signal type changes.
250 * @param tile The tile where the change occurred.
251 * @param track The track where the change occurred.
252 * @param old_type The old type of the changed signal
253 * @param new_type The new type of the changed signal
255 void SignalTypeChanged(TileIndex tile
, Track track
, SignalType old_type
, SignalType new_type
)
257 if (old_type
== SIGTYPE_LOGIC
) DeleteSignalProgram(tile
, track
);
258 if (new_type
== SIGTYPE_LOGIC
) CreateSignalProgram(tile
, track
);
262 * Executed whenever signal state has changed by the main program.
263 * @param tile Tile where the change occurred
264 * @param track Track where the change occurred
265 * @param depth Recursion depth, starts at 1.
267 void SignalStateChanged(TileIndex tile
, Track track
, int depth
)
269 SignalReference link
= FindSignalLink(tile
, track
);
271 SignalProgram
*program
= FindSignalProgram(GetTileFromSignalReference(link
), GetTrackFromSignalReference(link
));
272 program
->InputChanged(depth
);