00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "../../stdafx.h"
00013 #include "script_map.hpp"
00014 #include "script_station.hpp"
00015 #include "script_cargo.hpp"
00016 #include "../../station_base.h"
00017 #include "../../company_func.h"
00018 #include "../../script/squirrel_helper_type.hpp"
00019
00020 ScriptRoad::RoadVehicleType ScriptRoad::GetRoadVehicleTypeForCargo(CargoID cargo_type)
00021 {
00022 return ScriptCargo::HasCargoClass(cargo_type, ScriptCargo::CC_PASSENGERS) ? ROADVEHTYPE_BUS : ROADVEHTYPE_TRUCK;
00023 }
00024
00025 bool ScriptRoad::IsRoadTile(TileIndex tile)
00026 {
00027 if (!::IsValidTile(tile)) return false;
00028
00029 return (::IsTileType(tile, MP_ROAD) && ::GetRoadTileType(tile) != ROAD_TILE_DEPOT) ||
00030 IsDriveThroughRoadStationTile(tile);
00031 }
00032
00033 bool ScriptRoad::IsRoadDepotTile(TileIndex tile)
00034 {
00035 if (!::IsValidTile(tile)) return false;
00036
00037 return ::IsTileType(tile, MP_ROAD) && ::GetRoadTileType(tile) == ROAD_TILE_DEPOT &&
00038 (::RoadTypeToRoadTypes((::RoadType)GetCurrentRoadType()) & ::GetRoadTypes(tile)) != 0;
00039 }
00040
00041 bool ScriptRoad::IsRoadStationTile(TileIndex tile)
00042 {
00043 if (!::IsValidTile(tile)) return false;
00044
00045 return ::IsRoadStopTile(tile) && (::RoadTypeToRoadTypes((::RoadType)GetCurrentRoadType()) & ::GetRoadTypes(tile)) != 0;
00046 }
00047
00048 bool ScriptRoad::IsDriveThroughRoadStationTile(TileIndex tile)
00049 {
00050 if (!::IsValidTile(tile)) return false;
00051
00052 return ::IsDriveThroughStopTile(tile) && (::RoadTypeToRoadTypes((::RoadType)GetCurrentRoadType()) & ::GetRoadTypes(tile)) != 0;
00053 }
00054
00055 bool ScriptRoad::IsRoadTypeAvailable(RoadType road_type)
00056 {
00057 return ::HasRoadTypesAvail(_current_company, ::RoadTypeToRoadTypes((::RoadType)road_type));
00058 }
00059
00060 ScriptRoad::RoadType ScriptRoad::GetCurrentRoadType()
00061 {
00062 return (RoadType)ScriptObject::GetRoadType();
00063 }
00064
00065 void ScriptRoad::SetCurrentRoadType(RoadType road_type)
00066 {
00067 if (!IsRoadTypeAvailable(road_type)) return;
00068
00069 ScriptObject::SetRoadType((::RoadType)road_type);
00070 }
00071
00072 bool ScriptRoad::HasRoadType(TileIndex tile, RoadType road_type)
00073 {
00074 if (!ScriptMap::IsValidTile(tile)) return false;
00075 if (!IsRoadTypeAvailable(road_type)) return false;
00076 return ::GetAnyRoadBits(tile, (::RoadType)road_type, false) != ROAD_NONE;
00077 }
00078
00079 bool ScriptRoad::AreRoadTilesConnected(TileIndex t1, TileIndex t2)
00080 {
00081 if (!::IsValidTile(t1)) return false;
00082 if (!::IsValidTile(t2)) return false;
00083 if (!IsRoadTypeAvailable(GetCurrentRoadType())) return false;
00084
00085
00086 if ((abs((int)::TileX(t1) - (int)::TileX(t2)) + abs((int)::TileY(t1) - (int)::TileY(t2))) != 1) return false;
00087
00088 RoadBits r1 = ::GetAnyRoadBits(t1, ScriptObject::GetRoadType());
00089 RoadBits r2 = ::GetAnyRoadBits(t2, ScriptObject::GetRoadType());
00090
00091 uint dir_1 = (::TileX(t1) == ::TileX(t2)) ? (::TileY(t1) < ::TileY(t2) ? 2 : 0) : (::TileX(t1) < ::TileX(t2) ? 1 : 3);
00092 uint dir_2 = 2 ^ dir_1;
00093
00094 DisallowedRoadDirections drd2 = IsNormalRoadTile(t2) ? GetDisallowedRoadDirections(t2) : DRD_NONE;
00095
00096 return HasBit(r1, dir_1) && HasBit(r2, dir_2) && drd2 != DRD_BOTH && drd2 != (dir_1 > dir_2 ? DRD_SOUTHBOUND : DRD_NORTHBOUND);
00097 }
00098
00099
00100
00115 static bool CheckAutoExpandedRoadBits(const Array *existing, int32 start, int32 end)
00116 {
00117 return (start + end == 0) && (existing->size == 0 || existing->array[0] == start || existing->array[0] == end);
00118 }
00119
00130 static int32 LookupWithoutBuildOnSlopes(::Slope slope, const Array *existing, int32 start, int32 end)
00131 {
00132 switch (slope) {
00133
00134 case SLOPE_FLAT:
00135 return 1;
00136
00137
00138
00139
00140
00141 case SLOPE_NE: case SLOPE_SW:
00142 return (CheckAutoExpandedRoadBits(existing, start, end) && (start == 1 || end == 1)) ? (existing->size == 0 ? 2 : 1) : 0;
00143 case SLOPE_SE: case SLOPE_NW:
00144 return (CheckAutoExpandedRoadBits(existing, start, end) && (start != 1 && end != 1)) ? (existing->size == 0 ? 2 : 1) : 0;
00145
00146
00147 default:
00148 return 0;
00149 }
00150 }
00151
00157 static int32 RotateNeighbour(int32 neighbour)
00158 {
00159 switch (neighbour) {
00160 case -2: return -1;
00161 case -1: return 2;
00162 case 1: return -2;
00163 case 2: return 1;
00164 default: NOT_REACHED();
00165 }
00166 }
00167
00173 static RoadBits NeighbourToRoadBits(int32 neighbour)
00174 {
00175 switch (neighbour) {
00176 case -2: return ROAD_NW;
00177 case -1: return ROAD_NE;
00178 case 2: return ROAD_SE;
00179 case 1: return ROAD_SW;
00180 default: NOT_REACHED();
00181 }
00182 }
00183
00194 static int32 LookupWithBuildOnSlopes(::Slope slope, Array *existing, int32 start, int32 end)
00195 {
00196
00197 if (IsSteepSlope(slope)) {
00198 slope = SlopeWithOneCornerRaised(GetHighestSlopeCorner(slope));
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 ROAD_N:
00264 case ROAD_E:
00265 case ROAD_S:
00266
00267 return 0;
00268
00269 case ROAD_X:
00270 case ROAD_Y:
00271
00272 if ((existing_roadbits | new_roadbits) != new_roadbits) {
00273
00274
00275 return 0;
00276 }
00277
00278
00279 return ((start_roadbits & ROAD_E) && !(existing_roadbits & ROAD_W)) ? 2 : 1;
00280
00281 default:
00282
00283
00284
00285 if ((existing_roadbits | new_roadbits) == new_roadbits) return 1;
00286 return (existing_roadbits & ROAD_E) ? 0 : 1;
00287 }
00288
00289 case SLOPE_SW:
00290
00291 switch (new_roadbits) {
00292 case ROAD_N:
00293 case ROAD_E:
00294
00295 return 0;
00296
00297 case ROAD_X:
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 ScriptRoad::CanBuildConnectedRoadParts(ScriptTile::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 ScriptRoad::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 ScriptRoad::CanBuildConnectedRoadParts(ScriptTile::GetSlope(tile), existing, start - tile, end - tile);
00386 }
00387
00396 static bool NeighbourHasReachableRoad(::RoadTypes rts, TileIndex start_tile, DiagDirection neighbour)
00397 {
00398 TileIndex neighbour_tile = ::TileAddByDiagDir(start_tile, neighbour);
00399 if ((rts & ::GetRoadTypes(neighbour_tile)) == 0) return false;
00400
00401 switch (::GetTileType(neighbour_tile)) {
00402 case MP_ROAD:
00403 return (::GetRoadTileType(neighbour_tile) != ROAD_TILE_DEPOT);
00404
00405 case MP_STATION:
00406 if (::IsDriveThroughStopTile(neighbour_tile)) {
00407 return (::DiagDirToAxis(neighbour) == ::DiagDirToAxis(::GetRoadStopDir(neighbour_tile)));
00408 }
00409 return false;
00410
00411 default:
00412 return false;
00413 }
00414 }
00415
00416 int32 ScriptRoad::GetNeighbourRoadCount(TileIndex tile)
00417 {
00418 if (!::IsValidTile(tile)) return false;
00419 if (!IsRoadTypeAvailable(GetCurrentRoadType())) return false;
00420
00421 ::RoadTypes rts = ::RoadTypeToRoadTypes((::RoadType)GetCurrentRoadType());
00422 int32 neighbour = 0;
00423
00424 if (TileX(tile) > 0 && NeighbourHasReachableRoad(rts, tile, DIAGDIR_NE)) neighbour++;
00425 if (NeighbourHasReachableRoad(rts, tile, DIAGDIR_SE)) neighbour++;
00426 if (NeighbourHasReachableRoad(rts, tile, DIAGDIR_SW)) neighbour++;
00427 if (TileY(tile) > 0 && NeighbourHasReachableRoad(rts, tile, DIAGDIR_NW)) neighbour++;
00428
00429 return neighbour;
00430 }
00431
00432 TileIndex ScriptRoad::GetRoadDepotFrontTile(TileIndex depot)
00433 {
00434 if (!IsRoadDepotTile(depot)) return INVALID_TILE;
00435
00436 return depot + ::TileOffsByDiagDir(::GetRoadDepotDirection(depot));
00437 }
00438
00439 TileIndex ScriptRoad::GetRoadStationFrontTile(TileIndex station)
00440 {
00441 if (!IsRoadStationTile(station)) return INVALID_TILE;
00442
00443 return station + ::TileOffsByDiagDir(::GetRoadStopDir(station));
00444 }
00445
00446 TileIndex ScriptRoad::GetDriveThroughBackTile(TileIndex station)
00447 {
00448 if (!IsDriveThroughRoadStationTile(station)) return INVALID_TILE;
00449
00450 return station + ::TileOffsByDiagDir(::ReverseDiagDir(::GetRoadStopDir(station)));
00451 }
00452
00453 bool ScriptRoad::_BuildRoadInternal(TileIndex start, TileIndex end, bool one_way, bool full)
00454 {
00455 EnforcePrecondition(false, start != end);
00456 EnforcePrecondition(false, ::IsValidTile(start));
00457 EnforcePrecondition(false, ::IsValidTile(end));
00458 EnforcePrecondition(false, ::TileX(start) == ::TileX(end) || ::TileY(start) == ::TileY(end));
00459 EnforcePrecondition(false, !one_way || ScriptObject::GetRoadType() == ::ROADTYPE_ROAD);
00460 EnforcePrecondition(false, IsRoadTypeAvailable(GetCurrentRoadType()));
00461
00462 return ScriptObject::DoCommand(start, end, (::TileY(start) != ::TileY(end) ? 4 : 0) | (((start < end) == !full) ? 1 : 2) | (ScriptObject::GetRoadType() << 3) | ((one_way ? 1 : 0) << 5) | 1 << 6, CMD_BUILD_LONG_ROAD);
00463 }
00464
00465 bool ScriptRoad::BuildRoad(TileIndex start, TileIndex end)
00466 {
00467 return _BuildRoadInternal(start, end, false, false);
00468 }
00469
00470 bool ScriptRoad::BuildOneWayRoad(TileIndex start, TileIndex end)
00471 {
00472 return _BuildRoadInternal(start, end, true, false);
00473 }
00474
00475 bool ScriptRoad::BuildRoadFull(TileIndex start, TileIndex end)
00476 {
00477 return _BuildRoadInternal(start, end, false, true);
00478 }
00479
00480 bool ScriptRoad::BuildOneWayRoadFull(TileIndex start, TileIndex end)
00481 {
00482 return _BuildRoadInternal(start, end, true, true);
00483 }
00484
00485 bool ScriptRoad::BuildRoadDepot(TileIndex tile, TileIndex front)
00486 {
00487 EnforcePrecondition(false, tile != front);
00488 EnforcePrecondition(false, ::IsValidTile(tile));
00489 EnforcePrecondition(false, ::IsValidTile(front));
00490 EnforcePrecondition(false, ::TileX(tile) == ::TileX(front) || ::TileY(tile) == ::TileY(front));
00491 EnforcePrecondition(false, IsRoadTypeAvailable(GetCurrentRoadType()));
00492
00493 uint entrance_dir = (::TileX(tile) == ::TileX(front)) ? (::TileY(tile) < ::TileY(front) ? 1 : 3) : (::TileX(tile) < ::TileX(front) ? 2 : 0);
00494
00495 return ScriptObject::DoCommand(tile, entrance_dir | (ScriptObject::GetRoadType() << 2), 0, CMD_BUILD_ROAD_DEPOT);
00496 }
00497
00498 bool ScriptRoad::_BuildRoadStationInternal(TileIndex tile, TileIndex front, RoadVehicleType road_veh_type, bool drive_through, StationID station_id)
00499 {
00500 EnforcePrecondition(false, tile != front);
00501 EnforcePrecondition(false, ::IsValidTile(tile));
00502 EnforcePrecondition(false, ::IsValidTile(front));
00503 EnforcePrecondition(false, ::TileX(tile) == ::TileX(front) || ::TileY(tile) == ::TileY(front));
00504 EnforcePrecondition(false, station_id == ScriptStation::STATION_NEW || station_id == ScriptStation::STATION_JOIN_ADJACENT || ScriptStation::IsValidStation(station_id));
00505 EnforcePrecondition(false, road_veh_type == ROADVEHTYPE_BUS || road_veh_type == ROADVEHTYPE_TRUCK);
00506 EnforcePrecondition(false, IsRoadTypeAvailable(GetCurrentRoadType()));
00507
00508 uint entrance_dir;
00509 if (drive_through) {
00510 entrance_dir = ::TileY(tile) != ::TileY(front);
00511 } else {
00512 entrance_dir = (::TileX(tile) == ::TileX(front)) ? (::TileY(tile) < ::TileY(front) ? 1 : 3) : (::TileX(tile) < ::TileX(front) ? 2 : 0);
00513 }
00514
00515 uint p2 = station_id == ScriptStation::STATION_JOIN_ADJACENT ? 0 : 32;
00516 p2 |= drive_through ? 2 : 0;
00517 p2 |= road_veh_type == ROADVEHTYPE_TRUCK ? 1 : 0;
00518 p2 |= ::RoadTypeToRoadTypes(ScriptObject::GetRoadType()) << 2;
00519 p2 |= entrance_dir << 6;
00520 p2 |= (ScriptStation::IsValidStation(station_id) ? station_id : INVALID_STATION) << 16;
00521 return ScriptObject::DoCommand(tile, 1 | 1 << 8, p2, CMD_BUILD_ROAD_STOP);
00522 }
00523
00524 bool ScriptRoad::BuildRoadStation(TileIndex tile, TileIndex front, RoadVehicleType road_veh_type, StationID station_id)
00525 {
00526 return _BuildRoadStationInternal(tile, front, road_veh_type, false, station_id);
00527 }
00528
00529 bool ScriptRoad::BuildDriveThroughRoadStation(TileIndex tile, TileIndex front, RoadVehicleType road_veh_type, StationID station_id)
00530 {
00531 return _BuildRoadStationInternal(tile, front, road_veh_type, true, station_id);
00532 }
00533
00534 bool ScriptRoad::RemoveRoad(TileIndex start, TileIndex end)
00535 {
00536 EnforcePrecondition(false, ::IsValidTile(start));
00537 EnforcePrecondition(false, ::IsValidTile(end));
00538 EnforcePrecondition(false, ::TileX(start) == ::TileX(end) || ::TileY(start) == ::TileY(end));
00539 EnforcePrecondition(false, IsRoadTypeAvailable(GetCurrentRoadType()));
00540
00541 return ScriptObject::DoCommand(start, end, (::TileY(start) != ::TileY(end) ? 4 : 0) | (start < end ? 1 : 2) | (ScriptObject::GetRoadType() << 3), CMD_REMOVE_LONG_ROAD);
00542 }
00543
00544 bool ScriptRoad::RemoveRoadFull(TileIndex start, TileIndex end)
00545 {
00546 EnforcePrecondition(false, ::IsValidTile(start));
00547 EnforcePrecondition(false, ::IsValidTile(end));
00548 EnforcePrecondition(false, ::TileX(start) == ::TileX(end) || ::TileY(start) == ::TileY(end));
00549 EnforcePrecondition(false, IsRoadTypeAvailable(GetCurrentRoadType()));
00550
00551 return ScriptObject::DoCommand(start, end, (::TileY(start) != ::TileY(end) ? 4 : 0) | (start < end ? 2 : 1) | (ScriptObject::GetRoadType() << 3), CMD_REMOVE_LONG_ROAD);
00552 }
00553
00554 bool ScriptRoad::RemoveRoadDepot(TileIndex tile)
00555 {
00556 EnforcePrecondition(false, ::IsValidTile(tile));
00557 EnforcePrecondition(false, IsTileType(tile, MP_ROAD))
00558 EnforcePrecondition(false, GetRoadTileType(tile) == ROAD_TILE_DEPOT);
00559
00560 return ScriptObject::DoCommand(tile, 0, 0, CMD_LANDSCAPE_CLEAR);
00561 }
00562
00563 bool ScriptRoad::RemoveRoadStation(TileIndex tile)
00564 {
00565 EnforcePrecondition(false, ::IsValidTile(tile));
00566 EnforcePrecondition(false, IsTileType(tile, MP_STATION));
00567 EnforcePrecondition(false, IsRoadStop(tile));
00568
00569 return ScriptObject::DoCommand(tile, 1 | 1 << 8, GetRoadStopType(tile), CMD_REMOVE_ROAD_STOP);
00570 }
00571
00572 Money ScriptRoad::GetBuildCost(RoadType roadtype, BuildType build_type)
00573 {
00574 if (!ScriptRoad::IsRoadTypeAvailable(roadtype)) return -1;
00575
00576 switch (build_type) {
00577 case BT_ROAD: return ::GetPrice(PR_BUILD_ROAD, 1, NULL);
00578 case BT_DEPOT: return ::GetPrice(PR_BUILD_DEPOT_ROAD, 1, NULL);
00579 case BT_BUS_STOP: return ::GetPrice(PR_BUILD_STATION_BUS, 1, NULL);
00580 case BT_TRUCK_STOP: return ::GetPrice(PR_BUILD_STATION_TRUCK, 1, NULL);
00581 default: return -1;
00582 }
00583 }
00584
00585 uint16 ScriptRoad::GetMaintenanceCostFactor(RoadType roadtype)
00586 {
00587 if (!ScriptRoad::IsRoadTypeAvailable(roadtype)) return 0;
00588
00589 return roadtype == ROADTYPE_TRAM ? 3 : 2;
00590 }