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 "../../script/squirrel_helper_type.hpp"
00018
00019 ScriptRoad::RoadVehicleType ScriptRoad::GetRoadVehicleTypeForCargo(CargoID cargo_type)
00020 {
00021 return ScriptCargo::HasCargoClass(cargo_type, ScriptCargo::CC_PASSENGERS) ? ROADVEHTYPE_BUS : ROADVEHTYPE_TRUCK;
00022 }
00023
00024 bool ScriptRoad::IsRoadTile(TileIndex tile)
00025 {
00026 if (!::IsValidTile(tile)) return false;
00027
00028 return (::IsTileType(tile, MP_ROAD) && ::GetRoadTileType(tile) != ROAD_TILE_DEPOT) ||
00029 IsDriveThroughRoadStationTile(tile);
00030 }
00031
00032 bool ScriptRoad::IsRoadDepotTile(TileIndex tile)
00033 {
00034 if (!::IsValidTile(tile)) return false;
00035
00036 return ::IsTileType(tile, MP_ROAD) && ::GetRoadTileType(tile) == ROAD_TILE_DEPOT &&
00037 (::RoadTypeToRoadTypes((::RoadType)GetCurrentRoadType()) & ::GetRoadTypes(tile)) != 0;
00038 }
00039
00040 bool ScriptRoad::IsRoadStationTile(TileIndex tile)
00041 {
00042 if (!::IsValidTile(tile)) return false;
00043
00044 return ::IsRoadStopTile(tile) && (::RoadTypeToRoadTypes((::RoadType)GetCurrentRoadType()) & ::GetRoadTypes(tile)) != 0;
00045 }
00046
00047 bool ScriptRoad::IsDriveThroughRoadStationTile(TileIndex tile)
00048 {
00049 if (!::IsValidTile(tile)) return false;
00050
00051 return ::IsDriveThroughStopTile(tile) && (::RoadTypeToRoadTypes((::RoadType)GetCurrentRoadType()) & ::GetRoadTypes(tile)) != 0;
00052 }
00053
00054 bool ScriptRoad::IsRoadTypeAvailable(RoadType road_type)
00055 {
00056 return ::HasRoadTypesAvail(ScriptObject::GetCompany(), ::RoadTypeToRoadTypes((::RoadType)road_type));
00057 }
00058
00059 ScriptRoad::RoadType ScriptRoad::GetCurrentRoadType()
00060 {
00061 return (RoadType)ScriptObject::GetRoadType();
00062 }
00063
00064 void ScriptRoad::SetCurrentRoadType(RoadType road_type)
00065 {
00066 if (!IsRoadTypeAvailable(road_type)) return;
00067
00068 ScriptObject::SetRoadType((::RoadType)road_type);
00069 }
00070
00071 bool ScriptRoad::HasRoadType(TileIndex tile, RoadType road_type)
00072 {
00073 if (!ScriptMap::IsValidTile(tile)) return false;
00074 if (!IsRoadTypeAvailable(road_type)) return false;
00075 return ::GetAnyRoadBits(tile, (::RoadType)road_type, false) != ROAD_NONE;
00076 }
00077
00078 bool ScriptRoad::AreRoadTilesConnected(TileIndex t1, TileIndex t2)
00079 {
00080 if (!::IsValidTile(t1)) return false;
00081 if (!::IsValidTile(t2)) return false;
00082 if (!IsRoadTypeAvailable(GetCurrentRoadType())) return false;
00083
00084
00085 if ((abs((int)::TileX(t1) - (int)::TileX(t2)) + abs((int)::TileY(t1) - (int)::TileY(t2))) != 1) return false;
00086
00087 RoadBits r1 = ::GetAnyRoadBits(t1, ScriptObject::GetRoadType());
00088 RoadBits r2 = ::GetAnyRoadBits(t2, ScriptObject::GetRoadType());
00089
00090 uint dir_1 = (::TileX(t1) == ::TileX(t2)) ? (::TileY(t1) < ::TileY(t2) ? 2 : 0) : (::TileX(t1) < ::TileX(t2) ? 1 : 3);
00091 uint dir_2 = 2 ^ dir_1;
00092
00093 DisallowedRoadDirections drd2 = IsNormalRoadTile(t2) ? GetDisallowedRoadDirections(t2) : DRD_NONE;
00094
00095 return HasBit(r1, dir_1) && HasBit(r2, dir_2) && drd2 != DRD_BOTH && drd2 != (dir_1 > dir_2 ? DRD_SOUTHBOUND : DRD_NORTHBOUND);
00096 }
00097
00098
00099
00114 static bool CheckAutoExpandedRoadBits(const Array *existing, int32 start, int32 end)
00115 {
00116 return (start + end == 0) && (existing->size == 0 || existing->array[0] == start || existing->array[0] == end);
00117 }
00118
00129 static int32 LookupWithoutBuildOnSlopes(::Slope slope, const Array *existing, int32 start, int32 end)
00130 {
00131 switch (slope) {
00132
00133 case SLOPE_FLAT:
00134 return 1;
00135
00136
00137
00138
00139
00140 case SLOPE_NE: case SLOPE_SW:
00141 return (CheckAutoExpandedRoadBits(existing, start, end) && (start == 1 || end == 1)) ? (existing->size == 0 ? 2 : 1) : 0;
00142 case SLOPE_SE: case SLOPE_NW:
00143 return (CheckAutoExpandedRoadBits(existing, start, end) && (start != 1 && end != 1)) ? (existing->size == 0 ? 2 : 1) : 0;
00144
00145
00146 default:
00147 return 0;
00148 }
00149 }
00150
00156 static int32 RotateNeighbour(int32 neighbour)
00157 {
00158 switch (neighbour) {
00159 case -2: return -1;
00160 case -1: return 2;
00161 case 1: return -2;
00162 case 2: return 1;
00163 default: NOT_REACHED();
00164 }
00165 }
00166
00172 static RoadBits NeighbourToRoadBits(int32 neighbour)
00173 {
00174 switch (neighbour) {
00175 case -2: return ROAD_NW;
00176 case -1: return ROAD_NE;
00177 case 2: return ROAD_SE;
00178 case 1: return ROAD_SW;
00179 default: NOT_REACHED();
00180 }
00181 }
00182
00193 static int32 LookupWithBuildOnSlopes(::Slope slope, Array *existing, int32 start, int32 end)
00194 {
00195
00196 if (IsSteepSlope(slope)) {
00197 slope = SlopeWithOneCornerRaised(GetHighestSlopeCorner(slope));
00198 }
00199
00200
00201
00202
00203
00204 static const ::Slope base_slopes[] = {
00205 SLOPE_FLAT, SLOPE_W, SLOPE_W, SLOPE_SW,
00206 SLOPE_W, SLOPE_EW, SLOPE_SW, SLOPE_WSE,
00207 SLOPE_W, SLOPE_SW, SLOPE_EW, SLOPE_WSE,
00208 SLOPE_SW, SLOPE_WSE, SLOPE_WSE};
00209 static const byte base_rotates[] = {0, 0, 1, 0, 2, 0, 1, 0, 3, 3, 2, 3, 2, 2, 1};
00210
00211 if (slope >= (::Slope)lengthof(base_slopes)) {
00212
00213 return -1;
00214 }
00215 byte base_rotate = base_rotates[slope];
00216 slope = base_slopes[slope];
00217
00218
00219
00220 switch (slope) {
00221 case SLOPE_FLAT:
00222
00223 return 1;
00224
00225 case SLOPE_EW:
00226 case SLOPE_WSE:
00227
00228
00229 return 1;
00230
00231 case SLOPE_W:
00232 case SLOPE_SW:
00233
00234 break;
00235
00236 default:
00237
00238 return -1;
00239 }
00240
00241
00242 for (int j = 0; j < base_rotate; j++) {
00243 for (int i = 0; i < existing->size; i++) {
00244 existing->array[i] = RotateNeighbour(existing->array[i]);
00245 }
00246 start = RotateNeighbour(start);
00247 end = RotateNeighbour(end);
00248 }
00249
00250
00251 RoadBits start_roadbits = NeighbourToRoadBits(start);
00252 RoadBits new_roadbits = start_roadbits | NeighbourToRoadBits(end);
00253 RoadBits existing_roadbits = ROAD_NONE;
00254 for (int i = 0; i < existing->size; i++) {
00255 existing_roadbits |= NeighbourToRoadBits(existing->array[i]);
00256 }
00257
00258 switch (slope) {
00259 case SLOPE_W:
00260
00261 switch (new_roadbits) {
00262 case ROAD_N:
00263 case ROAD_E:
00264 case ROAD_S:
00265
00266 return 0;
00267
00268 case ROAD_X:
00269 case ROAD_Y:
00270
00271 if ((existing_roadbits | new_roadbits) != new_roadbits) {
00272
00273
00274 return 0;
00275 }
00276
00277
00278 return ((start_roadbits & ROAD_E) && !(existing_roadbits & ROAD_W)) ? 2 : 1;
00279
00280 default:
00281
00282
00283
00284 if ((existing_roadbits | new_roadbits) == new_roadbits) return 1;
00285 return (existing_roadbits & ROAD_E) ? 0 : 1;
00286 }
00287
00288 case SLOPE_SW:
00289
00290 switch (new_roadbits) {
00291 case ROAD_N:
00292 case ROAD_E:
00293
00294 return 0;
00295
00296 case ROAD_X:
00297
00298 if ((existing_roadbits | new_roadbits) != new_roadbits) {
00299
00300
00301 return 0;
00302 }
00303
00304
00305 return ((start_roadbits & ROAD_NE) && !(existing_roadbits & ROAD_SW)) ? 2 : 1;
00306
00307 default:
00308
00309
00310
00311 return (existing_roadbits & ROAD_NE) ? 0 : 1;
00312 }
00313
00314 default:
00315 NOT_REACHED();
00316 }
00317 }
00318
00329 static bool NormaliseTileOffset(int32 *tile)
00330 {
00331 if (*tile == 1 || *tile == -1) return true;
00332 if (*tile == ::TileDiffXY(0, -1)) {
00333 *tile = -2;
00334 return true;
00335 }
00336 if (*tile == ::TileDiffXY(0, 1)) {
00337 *tile = 2;
00338 return true;
00339 }
00340 return false;
00341 }
00342
00343 int32 ScriptRoad::CanBuildConnectedRoadParts(ScriptTile::Slope slope_, Array *existing, TileIndex start_, TileIndex end_)
00344 {
00345 ::Slope slope = (::Slope)slope_;
00346 int32 start = start_;
00347 int32 end = end_;
00348
00349
00350 if (start == end) return -1;
00351
00352 for (int i = 0; i < existing->size; i++) {
00353 if (!NormaliseTileOffset(&existing->array[i])) return -1;
00354 }
00355
00356 if (!NormaliseTileOffset(&start)) return -1;
00357 if (!NormaliseTileOffset(&end)) return -1;
00358
00359
00360
00361 return _settings_game.construction.build_on_slopes ? LookupWithBuildOnSlopes(slope, existing, start, end) : LookupWithoutBuildOnSlopes(slope, existing, start, end);
00362 }
00363
00364 int32 ScriptRoad::CanBuildConnectedRoadPartsHere(TileIndex tile, TileIndex start, TileIndex end)
00365 {
00366 if (!::IsValidTile(tile) || !::IsValidTile(start) || !::IsValidTile(end)) return -1;
00367 if (::DistanceManhattan(tile, start) != 1 || ::DistanceManhattan(tile, end) != 1) return -1;
00368
00369
00370 static const TileIndex neighbours[] = {::TileDiffXY(0, -1), ::TileDiffXY(1, 0), ::TileDiffXY(0, 1), ::TileDiffXY(-1, 0)};
00371 Array *existing = (Array*)alloca(sizeof(Array) + lengthof(neighbours) * sizeof(int32));
00372 existing->size = 0;
00373
00374 ::RoadBits rb = ::ROAD_NONE;
00375 if (::IsNormalRoadTile(tile)) {
00376 rb = ::GetAllRoadBits(tile);
00377 } else {
00378 for (::RoadType rt = ::ROADTYPE_BEGIN; rt < ::ROADTYPE_END; rt++) rb |= ::GetAnyRoadBits(tile, rt);
00379 }
00380 for (uint i = 0; i < lengthof(neighbours); i++) {
00381 if (HasBit(rb, i)) existing->array[existing->size++] = neighbours[i];
00382 }
00383
00384 return ScriptRoad::CanBuildConnectedRoadParts(ScriptTile::GetSlope(tile), existing, start - tile, end - tile);
00385 }
00386
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 ScriptRoad::GetNeighbourRoadCount(TileIndex tile)
00416 {
00417 if (!::IsValidTile(tile)) return false;
00418 if (!IsRoadTypeAvailable(GetCurrentRoadType())) return false;
00419
00420 ::RoadTypes rts = ::RoadTypeToRoadTypes((::RoadType)GetCurrentRoadType());
00421 int32 neighbour = 0;
00422
00423 if (TileX(tile) > 0 && NeighbourHasReachableRoad(rts, tile, DIAGDIR_NE)) neighbour++;
00424 if (NeighbourHasReachableRoad(rts, tile, DIAGDIR_SE)) neighbour++;
00425 if (NeighbourHasReachableRoad(rts, tile, DIAGDIR_SW)) neighbour++;
00426 if (TileY(tile) > 0 && NeighbourHasReachableRoad(rts, tile, DIAGDIR_NW)) neighbour++;
00427
00428 return neighbour;
00429 }
00430
00431 TileIndex ScriptRoad::GetRoadDepotFrontTile(TileIndex depot)
00432 {
00433 if (!IsRoadDepotTile(depot)) return INVALID_TILE;
00434
00435 return depot + ::TileOffsByDiagDir(::GetRoadDepotDirection(depot));
00436 }
00437
00438 TileIndex ScriptRoad::GetRoadStationFrontTile(TileIndex station)
00439 {
00440 if (!IsRoadStationTile(station)) return INVALID_TILE;
00441
00442 return station + ::TileOffsByDiagDir(::GetRoadStopDir(station));
00443 }
00444
00445 TileIndex ScriptRoad::GetDriveThroughBackTile(TileIndex station)
00446 {
00447 if (!IsDriveThroughRoadStationTile(station)) return INVALID_TILE;
00448
00449 return station + ::TileOffsByDiagDir(::ReverseDiagDir(::GetRoadStopDir(station)));
00450 }
00451
00452 bool ScriptRoad::_BuildRoadInternal(TileIndex start, TileIndex end, bool one_way, bool full)
00453 {
00454 EnforcePrecondition(false, start != end);
00455 EnforcePrecondition(false, ::IsValidTile(start));
00456 EnforcePrecondition(false, ::IsValidTile(end));
00457 EnforcePrecondition(false, ::TileX(start) == ::TileX(end) || ::TileY(start) == ::TileY(end));
00458 EnforcePrecondition(false, !one_way || ScriptObject::GetRoadType() == ::ROADTYPE_ROAD);
00459 EnforcePrecondition(false, IsRoadTypeAvailable(GetCurrentRoadType()));
00460
00461 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);
00462 }
00463
00464 bool ScriptRoad::BuildRoad(TileIndex start, TileIndex end)
00465 {
00466 return _BuildRoadInternal(start, end, false, false);
00467 }
00468
00469 bool ScriptRoad::BuildOneWayRoad(TileIndex start, TileIndex end)
00470 {
00471 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
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 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
00483 return _BuildRoadInternal(start, end, true, true);
00484 }
00485
00486 bool ScriptRoad::BuildRoadDepot(TileIndex tile, TileIndex front)
00487 {
00488 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
00489 EnforcePrecondition(false, tile != front);
00490 EnforcePrecondition(false, ::IsValidTile(tile));
00491 EnforcePrecondition(false, ::IsValidTile(front));
00492 EnforcePrecondition(false, ::TileX(tile) == ::TileX(front) || ::TileY(tile) == ::TileY(front));
00493 EnforcePrecondition(false, IsRoadTypeAvailable(GetCurrentRoadType()));
00494
00495 uint entrance_dir = (::TileX(tile) == ::TileX(front)) ? (::TileY(tile) < ::TileY(front) ? 1 : 3) : (::TileX(tile) < ::TileX(front) ? 2 : 0);
00496
00497 return ScriptObject::DoCommand(tile, entrance_dir | (ScriptObject::GetRoadType() << 2), 0, CMD_BUILD_ROAD_DEPOT);
00498 }
00499
00500 bool ScriptRoad::_BuildRoadStationInternal(TileIndex tile, TileIndex front, RoadVehicleType road_veh_type, bool drive_through, StationID station_id)
00501 {
00502 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
00503 EnforcePrecondition(false, tile != front);
00504 EnforcePrecondition(false, ::IsValidTile(tile));
00505 EnforcePrecondition(false, ::IsValidTile(front));
00506 EnforcePrecondition(false, ::TileX(tile) == ::TileX(front) || ::TileY(tile) == ::TileY(front));
00507 EnforcePrecondition(false, station_id == ScriptStation::STATION_NEW || station_id == ScriptStation::STATION_JOIN_ADJACENT || ScriptStation::IsValidStation(station_id));
00508 EnforcePrecondition(false, road_veh_type == ROADVEHTYPE_BUS || road_veh_type == ROADVEHTYPE_TRUCK);
00509 EnforcePrecondition(false, IsRoadTypeAvailable(GetCurrentRoadType()));
00510
00511 uint entrance_dir;
00512 if (drive_through) {
00513 entrance_dir = ::TileY(tile) != ::TileY(front);
00514 } else {
00515 entrance_dir = (::TileX(tile) == ::TileX(front)) ? (::TileY(tile) < ::TileY(front) ? 1 : 3) : (::TileX(tile) < ::TileX(front) ? 2 : 0);
00516 }
00517
00518 uint p2 = station_id == ScriptStation::STATION_JOIN_ADJACENT ? 0 : 32;
00519 p2 |= drive_through ? 2 : 0;
00520 p2 |= road_veh_type == ROADVEHTYPE_TRUCK ? 1 : 0;
00521 p2 |= ::RoadTypeToRoadTypes(ScriptObject::GetRoadType()) << 2;
00522 p2 |= entrance_dir << 6;
00523 p2 |= (ScriptStation::IsValidStation(station_id) ? station_id : INVALID_STATION) << 16;
00524 return ScriptObject::DoCommand(tile, 1 | 1 << 8, p2, CMD_BUILD_ROAD_STOP);
00525 }
00526
00527 bool ScriptRoad::BuildRoadStation(TileIndex tile, TileIndex front, RoadVehicleType road_veh_type, StationID station_id)
00528 {
00529 return _BuildRoadStationInternal(tile, front, road_veh_type, false, station_id);
00530 }
00531
00532 bool ScriptRoad::BuildDriveThroughRoadStation(TileIndex tile, TileIndex front, RoadVehicleType road_veh_type, StationID station_id)
00533 {
00534 return _BuildRoadStationInternal(tile, front, road_veh_type, true, station_id);
00535 }
00536
00537 bool ScriptRoad::RemoveRoad(TileIndex start, TileIndex end)
00538 {
00539 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
00540 EnforcePrecondition(false, ::IsValidTile(start));
00541 EnforcePrecondition(false, ::IsValidTile(end));
00542 EnforcePrecondition(false, ::TileX(start) == ::TileX(end) || ::TileY(start) == ::TileY(end));
00543 EnforcePrecondition(false, IsRoadTypeAvailable(GetCurrentRoadType()));
00544
00545 return ScriptObject::DoCommand(start, end, (::TileY(start) != ::TileY(end) ? 4 : 0) | (start < end ? 1 : 2) | (ScriptObject::GetRoadType() << 3), CMD_REMOVE_LONG_ROAD);
00546 }
00547
00548 bool ScriptRoad::RemoveRoadFull(TileIndex start, TileIndex end)
00549 {
00550 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
00551 EnforcePrecondition(false, ::IsValidTile(start));
00552 EnforcePrecondition(false, ::IsValidTile(end));
00553 EnforcePrecondition(false, ::TileX(start) == ::TileX(end) || ::TileY(start) == ::TileY(end));
00554 EnforcePrecondition(false, IsRoadTypeAvailable(GetCurrentRoadType()));
00555
00556 return ScriptObject::DoCommand(start, end, (::TileY(start) != ::TileY(end) ? 4 : 0) | (start < end ? 2 : 1) | (ScriptObject::GetRoadType() << 3), CMD_REMOVE_LONG_ROAD);
00557 }
00558
00559 bool ScriptRoad::RemoveRoadDepot(TileIndex tile)
00560 {
00561 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
00562 EnforcePrecondition(false, ::IsValidTile(tile));
00563 EnforcePrecondition(false, IsTileType(tile, MP_ROAD))
00564 EnforcePrecondition(false, GetRoadTileType(tile) == ROAD_TILE_DEPOT);
00565
00566 return ScriptObject::DoCommand(tile, 0, 0, CMD_LANDSCAPE_CLEAR);
00567 }
00568
00569 bool ScriptRoad::RemoveRoadStation(TileIndex tile)
00570 {
00571 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
00572 EnforcePrecondition(false, ::IsValidTile(tile));
00573 EnforcePrecondition(false, IsTileType(tile, MP_STATION));
00574 EnforcePrecondition(false, IsRoadStop(tile));
00575
00576 return ScriptObject::DoCommand(tile, 1 | 1 << 8, GetRoadStopType(tile), CMD_REMOVE_ROAD_STOP);
00577 }
00578
00579 Money ScriptRoad::GetBuildCost(RoadType roadtype, BuildType build_type)
00580 {
00581 if (!ScriptRoad::IsRoadTypeAvailable(roadtype)) return -1;
00582
00583 switch (build_type) {
00584 case BT_ROAD: return ::GetPrice(PR_BUILD_ROAD, 1, NULL);
00585 case BT_DEPOT: return ::GetPrice(PR_BUILD_DEPOT_ROAD, 1, NULL);
00586 case BT_BUS_STOP: return ::GetPrice(PR_BUILD_STATION_BUS, 1, NULL);
00587 case BT_TRUCK_STOP: return ::GetPrice(PR_BUILD_STATION_TRUCK, 1, NULL);
00588 default: return -1;
00589 }
00590 }
00591
00592 uint16 ScriptRoad::GetMaintenanceCostFactor(RoadType roadtype)
00593 {
00594 if (!ScriptRoad::IsRoadTypeAvailable(roadtype)) return 0;
00595
00596 return roadtype == ROADTYPE_TRAM ? 3 : 2;
00597 }