00001
00002
00005 #include "ai_road.hpp"
00006 #include "ai_map.hpp"
00007 #include "ai_station.hpp"
00008 #include "../../station_map.h"
00009 #include "../../command_type.h"
00010 #include "../../settings_type.h"
00011 #include "../../company_func.h"
00012 #include "../../script/squirrel_helper_type.hpp"
00013
00014 bool AIRoad::IsRoadTile(TileIndex tile)
00015 {
00016 if (!::IsValidTile(tile)) return false;
00017
00018 return (::IsTileType(tile, MP_ROAD) && ::GetRoadTileType(tile) != ROAD_TILE_DEPOT) ||
00019 IsDriveThroughRoadStationTile(tile);
00020 }
00021
00022 bool AIRoad::IsRoadDepotTile(TileIndex tile)
00023 {
00024 if (!::IsValidTile(tile)) return false;
00025
00026 return ::IsTileType(tile, MP_ROAD) && ::GetRoadTileType(tile) == ROAD_TILE_DEPOT &&
00027 (::RoadTypeToRoadTypes((::RoadType)GetCurrentRoadType()) & ::GetRoadTypes(tile)) != 0;
00028 }
00029
00030 bool AIRoad::IsRoadStationTile(TileIndex tile)
00031 {
00032 if (!::IsValidTile(tile)) return false;
00033
00034 return ::IsRoadStopTile(tile) && (::RoadTypeToRoadTypes((::RoadType)GetCurrentRoadType()) & ::GetRoadTypes(tile)) != 0;
00035 }
00036
00037 bool AIRoad::IsDriveThroughRoadStationTile(TileIndex tile)
00038 {
00039 if (!::IsValidTile(tile)) return false;
00040
00041 return ::IsDriveThroughStopTile(tile) && (::RoadTypeToRoadTypes((::RoadType)GetCurrentRoadType()) & ::GetRoadTypes(tile)) != 0;
00042 }
00043
00044 bool AIRoad::IsRoadTypeAvailable(RoadType road_type)
00045 {
00046 return ::HasRoadTypesAvail(_current_company, ::RoadTypeToRoadTypes((::RoadType)road_type));
00047 }
00048
00049 AIRoad::RoadType AIRoad::GetCurrentRoadType()
00050 {
00051 return (RoadType)AIObject::GetRoadType();
00052 }
00053
00054 void AIRoad::SetCurrentRoadType(RoadType road_type)
00055 {
00056 if (!IsRoadTypeAvailable(road_type)) return;
00057
00058 AIObject::SetRoadType((::RoadType)road_type);
00059 }
00060
00061 bool AIRoad::HasRoadType(TileIndex tile, RoadType road_type)
00062 {
00063 if (!AIMap::IsValidTile(tile)) return false;
00064 if (!IsRoadTypeAvailable(road_type)) return false;
00065 return ::GetAnyRoadBits(tile, (::RoadType)road_type, false) != ROAD_NONE;
00066 }
00067
00068 bool AIRoad::AreRoadTilesConnected(TileIndex t1, TileIndex t2)
00069 {
00070 if (!::IsValidTile(t1)) return false;
00071 if (!::IsValidTile(t2)) return false;
00072
00073
00074 if ((abs((int)::TileX(t1) - (int)::TileX(t2)) + abs((int)::TileY(t1) - (int)::TileY(t2))) != 1) return false;
00075
00076 RoadBits r1 = ::GetAnyRoadBits(t1, AIObject::GetRoadType());
00077 RoadBits r2 = ::GetAnyRoadBits(t2, AIObject::GetRoadType());
00078
00079 uint dir_1 = (::TileX(t1) == ::TileX(t2)) ? (::TileY(t1) < ::TileY(t2) ? 2 : 0) : (::TileX(t1) < ::TileX(t2) ? 1 : 3);
00080 uint dir_2 = 2 ^ dir_1;
00081
00082 DisallowedRoadDirections drd2 = IsNormalRoadTile(t2) ? GetDisallowedRoadDirections(t2) : DRD_NONE;
00083
00084 return HasBit(r1, dir_1) && HasBit(r2, dir_2) && drd2 != DRD_BOTH && drd2 != (dir_1 > dir_2 ? DRD_SOUTHBOUND : DRD_NORTHBOUND);
00085 }
00086
00087
00088
00103 static bool CheckAutoExpandedRoadBits(const Array *existing, int32 start, int32 end)
00104 {
00105 return (start + end == 0) && (existing->size == 0 || existing->array[0] == start || existing->array[0] == end);
00106 }
00107
00118 static int32 LookupWithoutBuildOnSlopes(::Slope slope, const Array *existing, int32 start, int32 end)
00119 {
00120 switch (slope) {
00121
00122 case SLOPE_FLAT:
00123 return 1;
00124
00125
00126
00127
00128
00129 case SLOPE_NE: case SLOPE_SW:
00130 return (CheckAutoExpandedRoadBits(existing, start, end) && (start == 1 || end == 1)) ? (existing->size == 0 ? 2 : 1) : 0;
00131 case SLOPE_SE: case SLOPE_NW:
00132 return (CheckAutoExpandedRoadBits(existing, start, end) && (start != 1 && end != 1)) ? (existing->size == 0 ? 2 : 1) : 0;
00133
00134
00135 default:
00136 return 0;
00137 }
00138 }
00139
00145 static int32 RotateNeighbour(int32 neighbour)
00146 {
00147 switch (neighbour) {
00148 case -2: return -1;
00149 case -1: return 2;
00150 case 1: return -2;
00151 case 2: return 1;
00152 default: NOT_REACHED();
00153 }
00154 }
00155
00161 static RoadBits NeighbourToRoadBits(int32 neighbour)
00162 {
00163 switch (neighbour) {
00164 case -2: return ROAD_NW;
00165 case -1: return ROAD_NE;
00166 case 2: return ROAD_SE;
00167 case 1: return ROAD_SW;
00168 default: NOT_REACHED();
00169 }
00170 }
00171
00182 static int32 LookupWithBuildOnSlopes(::Slope slope, Array *existing, int32 start, int32 end)
00183 {
00184 if (::IsSteepSlope(slope)) {
00185 switch (slope) {
00186
00187
00188
00189 case SLOPE_STEEP_S:
00190 case SLOPE_STEEP_W:
00191 case SLOPE_STEEP_N:
00192 case SLOPE_STEEP_E:
00193 return CheckAutoExpandedRoadBits(existing, start, end) ? (existing->size == 0 ? 2 : 1) : 0;
00194
00195
00196 default:
00197 return -1;
00198 }
00199 }
00200
00201
00202
00203
00204
00205 static const ::Slope base_slopes[] = {
00206 SLOPE_FLAT, SLOPE_W, SLOPE_W, SLOPE_SW,
00207 SLOPE_W, SLOPE_EW, SLOPE_SW, SLOPE_WSE,
00208 SLOPE_W, SLOPE_SW, SLOPE_EW, SLOPE_WSE,
00209 SLOPE_SW, SLOPE_WSE, SLOPE_WSE};
00210 static const byte base_rotates[] = {0, 0, 1, 0, 2, 0, 1, 0, 3, 3, 2, 3, 2, 2, 1};
00211
00212 if (slope >= (::Slope)lengthof(base_slopes)) {
00213
00214 return -1;
00215 }
00216 byte base_rotate = base_rotates[slope];
00217 slope = base_slopes[slope];
00218
00219
00220
00221 switch (slope) {
00222 case SLOPE_FLAT:
00223
00224 return 1;
00225
00226 case SLOPE_EW:
00227 case SLOPE_WSE:
00228
00229
00230 return 1;
00231
00232 case SLOPE_W:
00233 case SLOPE_SW:
00234
00235 break;
00236
00237 default:
00238
00239 return -1;
00240 }
00241
00242
00243 for (int j = 0; j < base_rotate; j++) {
00244 for (int i = 0; i < existing->size; i++) {
00245 existing->array[i] = RotateNeighbour(existing->array[i]);
00246 }
00247 start = RotateNeighbour(start);
00248 end = RotateNeighbour(end);
00249 }
00250
00251
00252 RoadBits start_roadbits = NeighbourToRoadBits(start);
00253 RoadBits new_roadbits = start_roadbits | NeighbourToRoadBits(end);
00254 RoadBits existing_roadbits = ROAD_NONE;
00255 for (int i = 0; i < existing->size; i++) {
00256 existing_roadbits |= NeighbourToRoadBits(existing->array[i]);
00257 }
00258
00259 switch (slope) {
00260 case SLOPE_W:
00261
00262 switch (new_roadbits) {
00263 case 6:
00264 case 9:
00265 case 12:
00266
00267 return 0;
00268
00269 case 5:
00270 case 10:
00271
00272 if ((existing_roadbits | new_roadbits) != new_roadbits) {
00273
00274
00275 return 0;
00276 }
00277
00278
00279 return ((start_roadbits & (ROAD_NE | ROAD_SE)) && !(existing_roadbits & (ROAD_SW | ROAD_NW))) ? 2 : 1;
00280
00281 default:
00282
00283
00284
00285 if ((existing_roadbits | new_roadbits) == new_roadbits) return 1;
00286 return (existing_roadbits & (ROAD_NE | ROAD_SE)) ? 0 : 1;
00287 }
00288
00289 case SLOPE_SW:
00290
00291 switch (new_roadbits) {
00292 case 9:
00293 case 12:
00294
00295 return 0;
00296
00297 case 10:
00298
00299 if ((existing_roadbits | new_roadbits) != new_roadbits) {
00300
00301
00302 return 0;
00303 }
00304
00305
00306 return ((start_roadbits & ROAD_NE) && !(existing_roadbits & ROAD_SW)) ? 2 : 1;
00307
00308 default:
00309
00310
00311
00312 return (existing_roadbits & ROAD_NE) ? 0 : 1;
00313 }
00314
00315 default:
00316 NOT_REACHED();
00317 }
00318 }
00319
00330 static bool NormaliseTileOffset(int32 *tile)
00331 {
00332 if (*tile == 1 || *tile == -1) return true;
00333 if (*tile == ::TileDiffXY(0, -1)) {
00334 *tile = -2;
00335 return true;
00336 }
00337 if (*tile == ::TileDiffXY(0, 1)) {
00338 *tile = 2;
00339 return true;
00340 }
00341 return false;
00342 }
00343
00344 int32 AIRoad::CanBuildConnectedRoadParts(AITile::Slope slope_, Array *existing, TileIndex start_, TileIndex end_)
00345 {
00346 ::Slope slope = (::Slope)slope_;
00347 int32 start = start_;
00348 int32 end = end_;
00349
00350
00351 if (start == end) return -1;
00352
00353 for (int i = 0; i < existing->size; i++) {
00354 if (!NormaliseTileOffset(&existing->array[i])) return -1;
00355 }
00356
00357 if (!NormaliseTileOffset(&start)) return -1;
00358 if (!NormaliseTileOffset(&end)) return -1;
00359
00360
00361
00362 return _settings_game.construction.build_on_slopes ? LookupWithBuildOnSlopes(slope, existing, start, end) : LookupWithoutBuildOnSlopes(slope, existing, start, end);
00363 }
00364
00365 int32 AIRoad::CanBuildConnectedRoadPartsHere(TileIndex tile, TileIndex start, TileIndex end)
00366 {
00367 if (!::IsValidTile(tile) || !::IsValidTile(start) || !::IsValidTile(end)) return -1;
00368 if (::DistanceManhattan(tile, start) != 1 || ::DistanceManhattan(tile, end) != 1) return -1;
00369
00370
00371 static const TileIndex neighbours[] = {::TileDiffXY(0, -1), ::TileDiffXY(1, 0), ::TileDiffXY(0, 1), ::TileDiffXY(-1, 0)};
00372 Array *existing = (Array*)alloca(sizeof(Array) + lengthof(neighbours) * sizeof(int32));
00373 existing->size = 0;
00374
00375 ::RoadBits rb = ::ROAD_NONE;
00376 if (::IsNormalRoadTile(tile)) {
00377 rb = ::GetAllRoadBits(tile);
00378 } else {
00379 for (::RoadType rt = ::ROADTYPE_BEGIN; rt < ::ROADTYPE_END; rt++) rb |= ::GetAnyRoadBits(tile, rt);
00380 }
00381 for (uint i = 0; i < lengthof(neighbours); i++) {
00382 if (HasBit(rb, i)) existing->array[existing->size++] = neighbours[i];
00383 }
00384
00385 return AIRoad::CanBuildConnectedRoadParts(AITile::GetSlope(tile), existing, start - tile, end - tile);
00386 }
00387
00395 static bool NeighbourHasReachableRoad(::RoadTypes rts, TileIndex start_tile, DiagDirection neighbour)
00396 {
00397 TileIndex neighbour_tile = ::TileAddByDiagDir(start_tile, neighbour);
00398 if ((rts & ::GetRoadTypes(neighbour_tile)) == 0) return false;
00399
00400 switch (::GetTileType(neighbour_tile)) {
00401 case MP_ROAD:
00402 return (::GetRoadTileType(neighbour_tile) != ROAD_TILE_DEPOT);
00403
00404 case MP_STATION:
00405 if (::IsDriveThroughStopTile(neighbour_tile)) {
00406 return (::DiagDirToAxis(neighbour) == ::DiagDirToAxis(::GetRoadStopDir(neighbour_tile)));
00407 }
00408 return false;
00409
00410 default:
00411 return false;
00412 }
00413 }
00414
00415 int32 AIRoad::GetNeighbourRoadCount(TileIndex tile)
00416 {
00417 if (!::IsValidTile(tile)) return false;
00418
00419 ::RoadTypes rts = ::RoadTypeToRoadTypes((::RoadType)GetCurrentRoadType());
00420 int32 neighbour = 0;
00421
00422 if (TileX(tile) > 0 && NeighbourHasReachableRoad(rts, tile, DIAGDIR_NE)) neighbour++;
00423 if (NeighbourHasReachableRoad(rts, tile, DIAGDIR_SE)) neighbour++;
00424 if (NeighbourHasReachableRoad(rts, tile, DIAGDIR_SW)) neighbour++;
00425 if (TileY(tile) > 0 && NeighbourHasReachableRoad(rts, tile, DIAGDIR_NW)) neighbour++;
00426
00427 return neighbour;
00428 }
00429
00430 TileIndex AIRoad::GetRoadDepotFrontTile(TileIndex depot)
00431 {
00432 if (!IsRoadDepotTile(depot)) return INVALID_TILE;
00433
00434 return depot + ::TileOffsByDiagDir(::GetRoadDepotDirection(depot));
00435 }
00436
00437 TileIndex AIRoad::GetRoadStationFrontTile(TileIndex station)
00438 {
00439 if (!IsRoadStationTile(station)) return INVALID_TILE;
00440
00441 return station + ::TileOffsByDiagDir(::GetRoadStopDir(station));
00442 }
00443
00444 TileIndex AIRoad::GetDriveThroughBackTile(TileIndex station)
00445 {
00446 if (!IsDriveThroughRoadStationTile(station)) return INVALID_TILE;
00447
00448 return station + ::TileOffsByDiagDir(::ReverseDiagDir(::GetRoadStopDir(station)));
00449 }
00450
00451 bool AIRoad::_BuildRoadInternal(TileIndex start, TileIndex end, bool one_way, bool full)
00452 {
00453 EnforcePrecondition(false, start != end);
00454 EnforcePrecondition(false, ::IsValidTile(start));
00455 EnforcePrecondition(false, ::IsValidTile(end));
00456 EnforcePrecondition(false, ::TileX(start) == ::TileX(end) || ::TileY(start) == ::TileY(end));
00457 EnforcePrecondition(false, !one_way || AIObject::GetRoadType() == ::ROADTYPE_ROAD);
00458
00459 return AIObject::DoCommand(end, start, (::TileY(start) != ::TileY(end) ? 4 : 0) | (((start < end) == !full) ? 1 : 2) | (AIObject::GetRoadType() << 3) | ((one_way ? 1 : 0) << 5), CMD_BUILD_LONG_ROAD);
00460 }
00461
00462 bool AIRoad::BuildRoad(TileIndex start, TileIndex end)
00463 {
00464 return _BuildRoadInternal(start, end, false, false);
00465 }
00466
00467 bool AIRoad::BuildOneWayRoad(TileIndex start, TileIndex end)
00468 {
00469 return _BuildRoadInternal(start, end, true, false);
00470 }
00471
00472 bool AIRoad::BuildRoadFull(TileIndex start, TileIndex end)
00473 {
00474 return _BuildRoadInternal(start, end, false, true);
00475 }
00476
00477 bool AIRoad::BuildOneWayRoadFull(TileIndex start, TileIndex end)
00478 {
00479 return _BuildRoadInternal(start, end, true, true);
00480 }
00481
00482 bool AIRoad::BuildRoadDepot(TileIndex tile, TileIndex front)
00483 {
00484 EnforcePrecondition(false, tile != front);
00485 EnforcePrecondition(false, ::IsValidTile(tile));
00486 EnforcePrecondition(false, ::IsValidTile(front));
00487 EnforcePrecondition(false, ::TileX(tile) == ::TileX(front) || ::TileY(tile) == ::TileY(front));
00488
00489 uint entrance_dir = (::TileX(tile) == ::TileX(front)) ? (::TileY(tile) < ::TileY(front) ? 1 : 3) : (::TileX(tile) < ::TileX(front) ? 2 : 0);
00490
00491 return AIObject::DoCommand(tile, entrance_dir | (AIObject::GetRoadType() << 2), 0, CMD_BUILD_ROAD_DEPOT);
00492 }
00493
00494 bool AIRoad::_BuildRoadStationInternal(TileIndex tile, TileIndex front, RoadVehicleType road_veh_type, bool drive_through, StationID station_id)
00495 {
00496 EnforcePrecondition(false, tile != front);
00497 EnforcePrecondition(false, ::IsValidTile(tile));
00498 EnforcePrecondition(false, ::IsValidTile(front));
00499 EnforcePrecondition(false, ::TileX(tile) == ::TileX(front) || ::TileY(tile) == ::TileY(front));
00500 EnforcePrecondition(false, station_id == AIStation::STATION_NEW || station_id == AIStation::STATION_JOIN_ADJACENT || AIStation::IsValidStation(station_id));
00501 EnforcePrecondition(false, road_veh_type == ROADVEHTYPE_BUS || road_veh_type == ROADVEHTYPE_TRUCK);
00502
00503 uint entrance_dir;
00504 if (drive_through) {
00505 entrance_dir = ::TileY(tile) != ::TileY(front);
00506 } else {
00507 entrance_dir = (::TileX(tile) == ::TileX(front)) ? (::TileY(tile) < ::TileY(front) ? 1 : 3) : (::TileX(tile) < ::TileX(front) ? 2 : 0);
00508 }
00509
00510 uint p2 = station_id == AIStation::STATION_JOIN_ADJACENT ? 0 : 32;
00511 p2 |= drive_through ? 2 : 0;
00512 p2 |= road_veh_type == ROADVEHTYPE_TRUCK ? 1 : 0;
00513 p2 |= ::RoadTypeToRoadTypes(AIObject::GetRoadType()) << 2;
00514 p2 |= (AIStation::IsValidStation(station_id) ? station_id : INVALID_STATION) << 16;
00515 return AIObject::DoCommand(tile, entrance_dir, p2, CMD_BUILD_ROAD_STOP);
00516 }
00517
00518 bool AIRoad::BuildRoadStation(TileIndex tile, TileIndex front, RoadVehicleType road_veh_type, StationID station_id)
00519 {
00520 return _BuildRoadStationInternal(tile, front, road_veh_type, false, station_id);
00521 }
00522
00523 bool AIRoad::BuildDriveThroughRoadStation(TileIndex tile, TileIndex front, RoadVehicleType road_veh_type, StationID station_id)
00524 {
00525 return _BuildRoadStationInternal(tile, front, road_veh_type, true, station_id);
00526 }
00527
00528 bool AIRoad::RemoveRoad(TileIndex start, TileIndex end)
00529 {
00530 EnforcePrecondition(false, ::IsValidTile(start));
00531 EnforcePrecondition(false, ::IsValidTile(end));
00532 EnforcePrecondition(false, ::TileX(start) == ::TileX(end) || ::TileY(start) == ::TileY(end));
00533
00534 return AIObject::DoCommand(end, start, (::TileY(start) != ::TileY(end) ? 4 : 0) | (start < end ? 1 : 2) | (AIObject::GetRoadType() << 3), CMD_REMOVE_LONG_ROAD);
00535 }
00536
00537 bool AIRoad::RemoveRoadFull(TileIndex start, TileIndex end)
00538 {
00539 EnforcePrecondition(false, ::IsValidTile(start));
00540 EnforcePrecondition(false, ::IsValidTile(end));
00541 EnforcePrecondition(false, ::TileX(start) == ::TileX(end) || ::TileY(start) == ::TileY(end));
00542
00543 return AIObject::DoCommand(end, start, (::TileY(start) != ::TileY(end) ? 4 : 0) | (start < end ? 2 : 1) | (AIObject::GetRoadType() << 3), CMD_REMOVE_LONG_ROAD);
00544 }
00545
00546 bool AIRoad::RemoveRoadDepot(TileIndex tile)
00547 {
00548 EnforcePrecondition(false, ::IsValidTile(tile));
00549 EnforcePrecondition(false, IsTileType(tile, MP_ROAD))
00550 EnforcePrecondition(false, GetRoadTileType(tile) == ROAD_TILE_DEPOT);
00551
00552 return AIObject::DoCommand(tile, 0, 0, CMD_LANDSCAPE_CLEAR);
00553 }
00554
00555 bool AIRoad::RemoveRoadStation(TileIndex tile)
00556 {
00557 EnforcePrecondition(false, ::IsValidTile(tile));
00558 EnforcePrecondition(false, IsTileType(tile, MP_STATION));
00559 EnforcePrecondition(false, IsRoadStop(tile));
00560
00561 return AIObject::DoCommand(tile, 0, GetRoadStopType(tile), CMD_REMOVE_ROAD_STOP);
00562 }