00001
00002
00005 #include "ai_rail.hpp"
00006 #include "ai_map.hpp"
00007 #include "ai_station.hpp"
00008 #include "../../debug.h"
00009 #include "../../station_map.h"
00010 #include "../../company_func.h"
00011 #include "../../waypoint.h"
00012 #include "../../newgrf_generic.h"
00013 #include "../../newgrf_station.h"
00014
00015 bool AIRail::IsRailTile(TileIndex tile)
00016 {
00017 if (!::IsValidTile(tile)) return false;
00018
00019 return (::IsTileType(tile, MP_RAILWAY) && !::IsRailDepot(tile)) ||
00020 (::IsRailwayStationTile(tile) && !::IsStationTileBlocked(tile)) || ::IsLevelCrossingTile(tile);
00021 }
00022
00023 bool AIRail::IsLevelCrossingTile(TileIndex tile)
00024 {
00025 if (!::IsValidTile(tile)) return false;
00026
00027 return ::IsLevelCrossingTile(tile);
00028 }
00029
00030 bool AIRail::IsRailDepotTile(TileIndex tile)
00031 {
00032 if (!::IsValidTile(tile)) return false;
00033
00034 return ::IsRailDepotTile(tile);
00035 }
00036
00037 bool AIRail::IsRailStationTile(TileIndex tile)
00038 {
00039 if (!::IsValidTile(tile)) return false;
00040
00041 return ::IsRailwayStationTile(tile);
00042 }
00043
00044 bool AIRail::IsRailWaypointTile(TileIndex tile)
00045 {
00046 if (!::IsValidTile(tile)) return false;
00047
00048 return ::IsTileType(tile, MP_RAILWAY) && ::IsRailWaypointTile(tile);
00049 }
00050
00051 bool AIRail::IsRailTypeAvailable(RailType rail_type)
00052 {
00053 if ((::RailType)rail_type < RAILTYPE_BEGIN || (::RailType)rail_type >= RAILTYPE_END) return false;
00054
00055 return ::HasRailtypeAvail(_current_company, (::RailType)rail_type);
00056 }
00057
00058 AIRail::RailType AIRail::GetCurrentRailType()
00059 {
00060 return (RailType)AIObject::GetRailType();
00061 }
00062
00063 void AIRail::SetCurrentRailType(RailType rail_type)
00064 {
00065 if (!IsRailTypeAvailable(rail_type)) return;
00066
00067 AIObject::SetRailType((::RailType)rail_type);
00068 }
00069
00070 bool AIRail::TrainCanRunOnRail(AIRail::RailType engine_rail_type, AIRail::RailType track_rail_type)
00071 {
00072 if (!AIRail::IsRailTypeAvailable(engine_rail_type)) return false;
00073 if (!AIRail::IsRailTypeAvailable(track_rail_type)) return false;
00074
00075 return ::IsCompatibleRail((::RailType)engine_rail_type, (::RailType)track_rail_type);
00076 }
00077
00078 bool AIRail::TrainHasPowerOnRail(AIRail::RailType engine_rail_type, AIRail::RailType track_rail_type)
00079 {\
00080 if (!AIRail::IsRailTypeAvailable(engine_rail_type)) return false;
00081 if (!AIRail::IsRailTypeAvailable(track_rail_type)) return false;
00082
00083 return ::HasPowerOnRail((::RailType)engine_rail_type, (::RailType)track_rail_type);
00084 }
00085
00086 AIRail::RailType AIRail::GetRailType(TileIndex tile)
00087 {
00088 if (!AITile::HasTransportType(tile, AITile::TRANSPORT_RAIL)) return RAILTYPE_INVALID;
00089
00090 return (RailType)::GetRailType(tile);
00091 }
00092
00093 bool AIRail::ConvertRailType(TileIndex start_tile, TileIndex end_tile, AIRail::RailType convert_to)
00094 {
00095 EnforcePrecondition(false, ::IsValidTile(start_tile));
00096 EnforcePrecondition(false, ::IsValidTile(end_tile));
00097 EnforcePrecondition(false, IsRailTypeAvailable(convert_to));
00098
00099 return AIObject::DoCommand(start_tile, end_tile, convert_to, CMD_CONVERT_RAIL);
00100 }
00101
00102 TileIndex AIRail::GetRailDepotFrontTile(TileIndex depot)
00103 {
00104 if (!IsRailDepotTile(depot)) return INVALID_TILE;
00105
00106 return depot + ::TileOffsByDiagDir(::GetRailDepotDirection(depot));
00107 }
00108
00109 AIRail::RailTrack AIRail::GetRailStationDirection(TileIndex tile)
00110 {
00111 if (!IsRailStationTile(tile)) return RAILTRACK_INVALID;
00112
00113 return (RailTrack)::GetRailStationTrack(tile);
00114 }
00115
00116 bool AIRail::BuildRailDepot(TileIndex tile, TileIndex front)
00117 {
00118 EnforcePrecondition(false, tile != front);
00119 EnforcePrecondition(false, ::IsValidTile(tile));
00120 EnforcePrecondition(false, ::IsValidTile(front));
00121 EnforcePrecondition(false, ::TileX(tile) == ::TileX(front) || ::TileY(tile) == ::TileY(front));
00122 EnforcePrecondition(false, IsRailTypeAvailable(GetCurrentRailType()));
00123
00124 uint entrance_dir = (::TileX(tile) == ::TileX(front)) ? (::TileY(tile) < ::TileY(front) ? 1 : 3) : (::TileX(tile) < ::TileX(front) ? 2 : 0);
00125
00126 return AIObject::DoCommand(tile, AIObject::GetRailType(), entrance_dir, CMD_BUILD_TRAIN_DEPOT);
00127 }
00128
00129 bool AIRail::BuildRailStation(TileIndex tile, RailTrack direction, uint num_platforms, uint platform_length, StationID station_id)
00130 {
00131 EnforcePrecondition(false, ::IsValidTile(tile));
00132 EnforcePrecondition(false, direction == RAILTRACK_NW_SE || direction == RAILTRACK_NE_SW);
00133 EnforcePrecondition(false, num_platforms > 0 && num_platforms <= 0xFF);
00134 EnforcePrecondition(false, platform_length > 0 && platform_length <= 0xFF);
00135 EnforcePrecondition(false, IsRailTypeAvailable(GetCurrentRailType()));
00136 EnforcePrecondition(false, station_id == AIStation::STATION_NEW || station_id == AIStation::STATION_JOIN_ADJACENT || AIStation::IsValidStation(station_id));
00137
00138 uint32 p1 = GetCurrentRailType() | (platform_length << 16) | (num_platforms << 8);
00139 if (direction == RAILTRACK_NW_SE) p1 |= (1 << 4);
00140 if (station_id != AIStation::STATION_JOIN_ADJACENT) p1 |= (1 << 24);
00141 return AIObject::DoCommand(tile, p1, (AIStation::IsValidStation(station_id) ? station_id : INVALID_STATION) << 16, CMD_BUILD_RAILROAD_STATION);
00142 }
00143
00144 bool AIRail::BuildNewGRFRailStation(TileIndex tile, RailTrack direction, uint num_platforms, uint platform_length, StationID station_id, CargoID cargo_id, IndustryType source_industry, IndustryType goal_industry, int distance, bool source_station)
00145 {
00146 EnforcePrecondition(false, ::IsValidTile(tile));
00147 EnforcePrecondition(false, direction == RAILTRACK_NW_SE || direction == RAILTRACK_NE_SW);
00148 EnforcePrecondition(false, num_platforms > 0 && num_platforms <= 0xFF);
00149 EnforcePrecondition(false, platform_length > 0 && platform_length <= 0xFF);
00150 EnforcePrecondition(false, IsRailTypeAvailable(GetCurrentRailType()));
00151 EnforcePrecondition(false, station_id == AIStation::STATION_NEW || station_id == AIStation::STATION_JOIN_ADJACENT || AIStation::IsValidStation(station_id));
00152
00153 uint32 p1 = GetCurrentRailType() | (platform_length << 16) | (num_platforms << 8);
00154 if (direction == RAILTRACK_NW_SE) p1 |= 1 << 4;
00155 if (station_id != AIStation::STATION_JOIN_ADJACENT) p1 |= (1 << 24);
00156
00157 const GRFFile *file;
00158 uint16 res = GetAiPurchaseCallbackResult(GSF_STATION, cargo_id, 0, source_industry, goal_industry, min(255, distance / 2), AICE_STATION_GET_STATION_ID, source_station ? 0 : 1, min(15, num_platforms) << 4 | min(15, platform_length), &file);
00159 uint32 p2 = (AIStation::IsValidStation(station_id) ? station_id : INVALID_STATION) << 16;
00160 if (res != CALLBACK_FAILED) {
00161 int index = 0;
00162 const StationSpec *spec = GetCustomStationSpecByGrf(file->grfid, res, &index);
00163 if (spec == NULL) {
00164 DEBUG(grf, 1, "%s returned an invalid station ID for 'AI construction/purchase selection (18)' callback", file->filename);
00165 } else {
00166 p2 |= spec->sclass | index << 8;
00167 }
00168
00169 }
00170 return AIObject::DoCommand(tile, p1, p2, CMD_BUILD_RAILROAD_STATION);
00171 }
00172
00173 bool AIRail::BuildRailWaypoint(TileIndex tile)
00174 {
00175 EnforcePrecondition(false, ::IsValidTile(tile));
00176 EnforcePrecondition(false, IsRailTile(tile));
00177 EnforcePrecondition(false, GetRailTracks(tile) == RAILTRACK_NE_SW || GetRailTracks(tile) == RAILTRACK_NW_SE);
00178 EnforcePrecondition(false, IsRailTypeAvailable(GetCurrentRailType()));
00179
00180 return AIObject::DoCommand(tile, 0, 0, CMD_BUILD_TRAIN_WAYPOINT);
00181 }
00182
00183 bool AIRail::RemoveRailWaypoint(TileIndex tile)
00184 {
00185 EnforcePrecondition(false, ::IsValidTile(tile));
00186 EnforcePrecondition(false, IsRailWaypointTile(tile));
00187
00188 return AIObject::DoCommand(tile, 0, 0, CMD_REMOVE_TRAIN_WAYPOINT);
00189 }
00190
00191 bool AIRail::RemoveRailStationTileRect(TileIndex tile, TileIndex tile2)
00192 {
00193 EnforcePrecondition(false, ::IsValidTile(tile));
00194 EnforcePrecondition(false, ::IsValidTile(tile2));
00195
00196 return AIObject::DoCommand(tile, tile2, 0, CMD_REMOVE_FROM_RAILROAD_STATION);
00197 }
00198
00199 uint AIRail::GetRailTracks(TileIndex tile)
00200 {
00201 if (!IsRailTile(tile)) return RAILTRACK_INVALID;
00202
00203 if (IsRailWaypointTile(tile)) return ::GetRailWaypointBits(tile);
00204 if (IsRailStationTile(tile)) return ::TrackToTrackBits(::GetRailStationTrack(tile));
00205 if (IsLevelCrossingTile(tile)) return ::GetCrossingRailBits(tile);
00206 return ::GetTrackBits(tile);
00207 }
00208
00209 bool AIRail::BuildRailTrack(TileIndex tile, RailTrack rail_track)
00210 {
00211 EnforcePrecondition(false, ::IsValidTile(tile));
00212 EnforcePrecondition(false, rail_track != 0);
00213 EnforcePrecondition(false, (rail_track & ~::TRACK_BIT_ALL) == 0);
00214 EnforcePrecondition(false, KillFirstBit((uint)rail_track) == 0);
00215 EnforcePrecondition(false, IsRailTypeAvailable(GetCurrentRailType()));
00216
00217 return AIObject::DoCommand(tile, tile, GetCurrentRailType() | (FindFirstTrack((::TrackBits)rail_track) << 4), CMD_BUILD_RAILROAD_TRACK);
00218 }
00219
00220 bool AIRail::RemoveRailTrack(TileIndex tile, RailTrack rail_track)
00221 {
00222 EnforcePrecondition(false, ::IsValidTile(tile));
00223 EnforcePrecondition(false, ::IsTileType(tile, MP_RAILWAY) && ::IsPlainRailTile(tile));
00224 EnforcePrecondition(false, GetRailTracks(tile) & rail_track);
00225 EnforcePrecondition(false, KillFirstBit((uint)rail_track) == 0);
00226
00227 return AIObject::DoCommand(tile, tile, GetCurrentRailType() | (FindFirstTrack((::TrackBits)rail_track) << 4), CMD_REMOVE_RAILROAD_TRACK);
00228 }
00229
00230 bool AIRail::AreTilesConnected(TileIndex from, TileIndex tile, TileIndex to)
00231 {
00232 if (!IsRailTile(tile)) return false;
00233 if (from == to || AIMap::DistanceManhattan(from, tile) != 1 || AIMap::DistanceManhattan(tile, to) != 1) return false;
00234
00235 if (to < from) ::Swap(from, to);
00236
00237 if (tile - from == 1) {
00238 if (to - tile == 1) return (GetRailTracks(tile) & RAILTRACK_NE_SW) != 0;
00239 if (to - tile == ::MapSizeX()) return (GetRailTracks(tile) & RAILTRACK_NE_SE) != 0;
00240 } else if (tile - from == ::MapSizeX()) {
00241 if (tile - to == 1) return (GetRailTracks(tile) & RAILTRACK_NW_NE) != 0;
00242 if (to - tile == 1) return (GetRailTracks(tile) & RAILTRACK_NW_SW) != 0;
00243 if (to - tile == ::MapSizeX()) return (GetRailTracks(tile) & RAILTRACK_NW_SE) != 0;
00244 } else {
00245 return (GetRailTracks(tile) & RAILTRACK_SW_SE) != 0;
00246 }
00247
00248 NOT_REACHED();
00249 }
00250
00255 static uint32 SimulateDrag(TileIndex from, TileIndex tile, TileIndex *to)
00256 {
00257 int diag_offset = abs(abs((int)::TileX(*to) - (int)::TileX(tile)) - abs((int)::TileY(*to) - (int)::TileY(tile)));
00258 uint32 p2 = AIRail::GetCurrentRailType();
00259 if (::TileY(from) == ::TileY(*to)) {
00260 p2 |= (TRACK_X << 4);
00261 *to -= Clamp((int)::TileX(*to) - (int)::TileX(tile), -1, 1);
00262 } else if (::TileX(from) == ::TileX(*to)) {
00263 p2 |= (TRACK_Y << 4);
00264 *to -= ::MapSizeX() * Clamp((int)::TileY(*to) - (int)::TileY(tile), -1, 1);
00265 } else if (::TileY(from) < ::TileY(tile)) {
00266 if (::TileX(*to) < ::TileX(tile)) {
00267 p2 |= (TRACK_UPPER << 4);
00268 } else {
00269 p2 |= (TRACK_LEFT << 4);
00270 }
00271 if (diag_offset) {
00272 *to -= Clamp((int)::TileX(*to) - (int)::TileX(tile), -1, 1);
00273 } else {
00274 *to -= ::MapSizeX() * Clamp((int)::TileY(*to) - (int)::TileY(tile), -1, 1);
00275 }
00276 } else if (::TileY(from) > ::TileY(tile)) {
00277 if (::TileX(*to) < ::TileX(tile)) {
00278 p2 |= (TRACK_RIGHT << 4);
00279 } else {
00280 p2 |= (TRACK_LOWER << 4);
00281 }
00282 if (diag_offset) {
00283 *to -= Clamp((int)::TileX(*to) - (int)::TileX(tile), -1, 1);
00284 } else {
00285 *to -= ::MapSizeX() * Clamp((int)::TileY(*to) - (int)::TileY(tile), -1, 1);
00286 }
00287 } else if (::TileX(from) < ::TileX(tile)) {
00288 if (::TileY(*to) < ::TileY(tile)) {
00289 p2 |= (TRACK_UPPER << 4);
00290 } else {
00291 p2 |= (TRACK_RIGHT << 4);
00292 }
00293 if (!diag_offset) {
00294 *to -= Clamp((int)::TileX(*to) - (int)::TileX(tile), -1, 1);
00295 } else {
00296 *to -= ::MapSizeX() * Clamp((int)::TileY(*to) - (int)::TileY(tile), -1, 1);
00297 }
00298 } else if (::TileX(from) > ::TileX(tile)) {
00299 if (::TileY(*to) < ::TileY(tile)) {
00300 p2 |= (TRACK_LEFT << 4);
00301 } else {
00302 p2 |= (TRACK_LOWER << 4);
00303 }
00304 if (!diag_offset) {
00305 *to -= Clamp((int)::TileX(*to) - (int)::TileX(tile), -1, 1);
00306 } else {
00307 *to -= ::MapSizeX() * Clamp((int)::TileY(*to) - (int)::TileY(tile), -1, 1);
00308 }
00309 }
00310 return p2;
00311 }
00312
00313 bool AIRail::BuildRail(TileIndex from, TileIndex tile, TileIndex to)
00314 {
00315 EnforcePrecondition(false, ::IsValidTile(from));
00316 EnforcePrecondition(false, ::IsValidTile(tile));
00317 EnforcePrecondition(false, ::IsValidTile(to));
00318 EnforcePrecondition(false, ::DistanceManhattan(from, tile) == 1);
00319 EnforcePrecondition(false, ::DistanceManhattan(tile,to) >= 1);
00320 EnforcePrecondition(false, IsRailTypeAvailable(GetCurrentRailType()));
00321 int diag_offset = abs(abs((int)::TileX(to) - (int)::TileX(tile)) - abs((int)::TileY(to) - (int)::TileY(tile)));
00322 EnforcePrecondition(false, diag_offset <= 1 ||
00323 (::TileX(from) == ::TileX(tile) && ::TileX(tile) == ::TileX(to)) ||
00324 (::TileY(from) == ::TileY(tile) && ::TileY(tile) == ::TileY(to)));
00325
00326 uint32 p2 = SimulateDrag(from, tile, &to);
00327 return AIObject::DoCommand(tile, to, p2, CMD_BUILD_RAILROAD_TRACK);
00328 }
00329
00330 bool AIRail::RemoveRail(TileIndex from, TileIndex tile, TileIndex to)
00331 {
00332 EnforcePrecondition(false, ::IsValidTile(from));
00333 EnforcePrecondition(false, ::IsValidTile(tile));
00334 EnforcePrecondition(false, ::IsValidTile(to));
00335 EnforcePrecondition(false, ::DistanceManhattan(from, tile) == 1);
00336 EnforcePrecondition(false, ::DistanceManhattan(tile,to) >= 1);
00337 int diag_offset = abs(abs((int)::TileX(to) - (int)::TileX(tile)) - abs((int)::TileY(to) - (int)::TileY(tile)));
00338 EnforcePrecondition(false, diag_offset <= 1 ||
00339 (::TileX(from) == ::TileX(tile) && ::TileX(tile) == ::TileX(to)) ||
00340 (::TileY(from) == ::TileY(tile) && ::TileY(tile) == ::TileY(to)));
00341
00342 if (!IsRailTypeAvailable(GetCurrentRailType())) SetCurrentRailType(GetRailType(tile));
00343 uint32 p2 = SimulateDrag(from, tile, &to);
00344 return AIObject::DoCommand(tile, to, p2, CMD_REMOVE_RAILROAD_TRACK);
00345 }
00346
00351 struct AIRailSignalData {
00352 Track track;
00353 Trackdir trackdir;
00354 uint signal_cycles;
00355 };
00356
00357 static const int NUM_TRACK_DIRECTIONS = 3;
00358
00365 static const AIRailSignalData _possible_trackdirs[5][NUM_TRACK_DIRECTIONS] = {
00366 {{TRACK_UPPER, TRACKDIR_UPPER_E, 0}, {TRACK_Y, TRACKDIR_Y_SE, 0}, {TRACK_LEFT, TRACKDIR_LEFT_S, 1}},
00367 {{TRACK_RIGHT, TRACKDIR_RIGHT_S, 1}, {TRACK_X, TRACKDIR_X_SW, 1}, {TRACK_UPPER, TRACKDIR_UPPER_W, 1}},
00368 {{INVALID_TRACK, INVALID_TRACKDIR, 0}, {INVALID_TRACK, INVALID_TRACKDIR, 0}, {INVALID_TRACK, INVALID_TRACKDIR, 0}},
00369 {{TRACK_LOWER, TRACKDIR_LOWER_E, 0}, {TRACK_X, TRACKDIR_X_NE, 0}, {TRACK_LEFT, TRACKDIR_LEFT_N, 0}},
00370 {{TRACK_RIGHT, TRACKDIR_RIGHT_N, 0}, {TRACK_Y, TRACKDIR_Y_NW, 1}, {TRACK_LOWER, TRACKDIR_LOWER_W, 1}}
00371 };
00372
00373 AIRail::SignalType AIRail::GetSignalType(TileIndex tile, TileIndex front)
00374 {
00375 if (AIMap::DistanceManhattan(tile, front) != 1) return SIGNALTYPE_NONE;
00376 if (!::IsTileType(tile, MP_RAILWAY) || !::HasSignals(tile)) return SIGNALTYPE_NONE;
00377
00378 int data_index = 2 + (::TileX(front) - ::TileX(tile)) + 2 * (::TileY(front) - ::TileY(tile));
00379
00380 for (int i = 0; i < NUM_TRACK_DIRECTIONS; i++) {
00381 const Track &track = _possible_trackdirs[data_index][i].track;
00382 if (!(::TrackToTrackBits(track) & GetRailTracks(tile))) continue;
00383 if (!HasSignalOnTrack(tile, track)) continue;
00384 if (!HasSignalOnTrackdir(tile, _possible_trackdirs[data_index][i].trackdir)) continue;
00385 SignalType st = (SignalType)::GetSignalType(tile, track);
00386 if (HasSignalOnTrackdir(tile, ::ReverseTrackdir(_possible_trackdirs[data_index][i].trackdir))) st = (SignalType)(st | SIGNALTYPE_TWOWAY);
00387 return st;
00388 }
00389
00390 return SIGNALTYPE_NONE;
00391 }
00392
00396 static bool IsValidSignalType(int signal_type)
00397 {
00398 if (signal_type < AIRail::SIGNALTYPE_NORMAL || signal_type > AIRail::SIGNALTYPE_COMBO_TWOWAY) return false;
00399 if (signal_type > AIRail::SIGNALTYPE_PBS_ONEWAY && signal_type < AIRail::SIGNALTYPE_NORMAL_TWOWAY) return false;
00400 return true;
00401 }
00402
00403 bool AIRail::BuildSignal(TileIndex tile, TileIndex front, SignalType signal)
00404 {
00405 EnforcePrecondition(false, AIMap::DistanceManhattan(tile, front) == 1)
00406 EnforcePrecondition(false, ::IsTileType(tile, MP_RAILWAY) && ::IsPlainRailTile(tile));
00407 EnforcePrecondition(false, ::IsValidSignalType(signal));
00408
00409 Track track = INVALID_TRACK;
00410 uint signal_cycles;
00411
00412 int data_index = 2 + (::TileX(front) - ::TileX(tile)) + 2 * (::TileY(front) - ::TileY(tile));
00413 for (int i = 0; i < NUM_TRACK_DIRECTIONS; i++) {
00414 const Track &t = _possible_trackdirs[data_index][i].track;
00415 if (!(::TrackToTrackBits(t) & GetRailTracks(tile))) continue;
00416 track = t;
00417 signal_cycles = _possible_trackdirs[data_index][i].signal_cycles;
00418 break;
00419 }
00420 EnforcePrecondition(false, track != INVALID_TRACK);
00421
00422 uint p1 = track;
00423 if (signal < SIGNALTYPE_TWOWAY) {
00424 if (signal != SIGNALTYPE_PBS && signal != SIGNALTYPE_PBS_ONEWAY) signal_cycles++;
00425 p1 |= (signal_cycles << 15);
00426 }
00427 p1 |= ((signal >= SIGNALTYPE_TWOWAY ? signal ^ SIGNALTYPE_TWOWAY : signal) << 5);
00428
00429 return AIObject::DoCommand(tile, p1, 0, CMD_BUILD_SIGNALS);
00430 }
00431
00432 bool AIRail::RemoveSignal(TileIndex tile, TileIndex front)
00433 {
00434 EnforcePrecondition(false, AIMap::DistanceManhattan(tile, front) == 1)
00435 EnforcePrecondition(false, GetSignalType(tile, front) != SIGNALTYPE_NONE);
00436
00437 Track track = INVALID_TRACK;
00438 int data_index = 2 + (::TileX(front) - ::TileX(tile)) + 2 * (::TileY(front) - ::TileY(tile));
00439 for (int i = 0; i < NUM_TRACK_DIRECTIONS; i++) {
00440 const Track &t = _possible_trackdirs[data_index][i].track;
00441 if (!(::TrackToTrackBits(t) & GetRailTracks(tile))) continue;
00442 track = t;
00443 break;
00444 }
00445 EnforcePrecondition(false, track != INVALID_TRACK);
00446
00447 return AIObject::DoCommand(tile, track, 0, CMD_REMOVE_SIGNALS);
00448 }