00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include "roadveh.h"
00014 #include "command_func.h"
00015 #include "news_func.h"
00016 #include "pathfinder/npf/npf_func.h"
00017 #include "station_base.h"
00018 #include "company_func.h"
00019 #include "articulated_vehicles.h"
00020 #include "newgrf_sound.h"
00021 #include "pathfinder/yapf/yapf.h"
00022 #include "strings_func.h"
00023 #include "tunnelbridge_map.h"
00024 #include "date_func.h"
00025 #include "vehicle_func.h"
00026 #include "sound_func.h"
00027 #include "ai/ai.hpp"
00028 #include "game/game.hpp"
00029 #include "depot_map.h"
00030 #include "effectvehicle_func.h"
00031 #include "roadstop_base.h"
00032 #include "spritecache.h"
00033 #include "core/random_func.hpp"
00034 #include "company_base.h"
00035 #include "core/backup_type.hpp"
00036 #include "newgrf.h"
00037 #include "zoom_func.h"
00038
00039 #include "table/strings.h"
00040
00041 static const uint16 _roadveh_images[] = {
00042 0xCD4, 0xCDC, 0xCE4, 0xCEC, 0xCF4, 0xCFC, 0xD0C, 0xD14,
00043 0xD24, 0xD1C, 0xD2C, 0xD04, 0xD1C, 0xD24, 0xD6C, 0xD74,
00044 0xD7C, 0xC14, 0xC1C, 0xC24, 0xC2C, 0xC34, 0xC3C, 0xC4C,
00045 0xC54, 0xC64, 0xC5C, 0xC6C, 0xC44, 0xC5C, 0xC64, 0xCAC,
00046 0xCB4, 0xCBC, 0xD94, 0xD9C, 0xDA4, 0xDAC, 0xDB4, 0xDBC,
00047 0xDCC, 0xDD4, 0xDE4, 0xDDC, 0xDEC, 0xDC4, 0xDDC, 0xDE4,
00048 0xE2C, 0xE34, 0xE3C, 0xC14, 0xC1C, 0xC2C, 0xC3C, 0xC4C,
00049 0xC5C, 0xC64, 0xC6C, 0xC74, 0xC84, 0xC94, 0xCA4
00050 };
00051
00052 static const uint16 _roadveh_full_adder[] = {
00053 0, 88, 0, 0, 0, 0, 48, 48,
00054 48, 48, 0, 0, 64, 64, 0, 16,
00055 16, 0, 88, 0, 0, 0, 0, 48,
00056 48, 48, 48, 0, 0, 64, 64, 0,
00057 16, 16, 0, 88, 0, 0, 0, 0,
00058 48, 48, 48, 48, 0, 0, 64, 64,
00059 0, 16, 16, 0, 8, 8, 8, 8,
00060 0, 0, 0, 8, 8, 8, 8
00061 };
00062 assert_compile(lengthof(_roadveh_images) == lengthof(_roadveh_full_adder));
00063
00064 template <>
00065 bool IsValidImageIndex<VEH_ROAD>(uint8 image_index)
00066 {
00067 return image_index < lengthof(_roadveh_images);
00068 }
00069
00071 static const TrackdirBits _road_enter_dir_to_reachable_trackdirs[DIAGDIR_END] = {
00072 TRACKDIR_BIT_LEFT_N | TRACKDIR_BIT_LOWER_E | TRACKDIR_BIT_X_NE,
00073 TRACKDIR_BIT_LEFT_S | TRACKDIR_BIT_UPPER_E | TRACKDIR_BIT_Y_SE,
00074 TRACKDIR_BIT_UPPER_W | TRACKDIR_BIT_X_SW | TRACKDIR_BIT_RIGHT_S,
00075 TRACKDIR_BIT_RIGHT_N | TRACKDIR_BIT_LOWER_W | TRACKDIR_BIT_Y_NW
00076 };
00077
00078 static const Trackdir _road_reverse_table[DIAGDIR_END] = {
00079 TRACKDIR_RVREV_NE, TRACKDIR_RVREV_SE, TRACKDIR_RVREV_SW, TRACKDIR_RVREV_NW
00080 };
00081
00083 static const Trackdir _roadveh_depot_exit_trackdir[DIAGDIR_END] = {
00084 TRACKDIR_X_NE, TRACKDIR_Y_SE, TRACKDIR_X_SW, TRACKDIR_Y_NW
00085 };
00086
00087
00092 bool RoadVehicle::IsBus() const
00093 {
00094 assert(this->IsFrontEngine());
00095 return IsCargoInClass(this->cargo_type, CC_PASSENGERS);
00096 }
00097
00103 int RoadVehicle::GetDisplayImageWidth(Point *offset) const
00104 {
00105 int reference_width = ROADVEHINFO_DEFAULT_VEHICLE_WIDTH;
00106
00107 if (offset != NULL) {
00108 offset->x = reference_width / 2;
00109 offset->y = 0;
00110 }
00111 return this->gcache.cached_veh_length * reference_width / VEHICLE_LENGTH;
00112 }
00113
00114 static SpriteID GetRoadVehIcon(EngineID engine, EngineImageType image_type)
00115 {
00116 const Engine *e = Engine::Get(engine);
00117 uint8 spritenum = e->u.road.image_index;
00118
00119 if (is_custom_sprite(spritenum)) {
00120 SpriteID sprite = GetCustomVehicleIcon(engine, DIR_W, image_type);
00121 if (sprite != 0) return sprite;
00122
00123 spritenum = e->original_image_index;
00124 }
00125
00126 assert(IsValidImageIndex<VEH_ROAD>(spritenum));
00127 return DIR_W + _roadveh_images[spritenum];
00128 }
00129
00130 SpriteID RoadVehicle::GetImage(Direction direction, EngineImageType image_type) const
00131 {
00132 uint8 spritenum = this->spritenum;
00133 SpriteID sprite;
00134
00135 if (is_custom_sprite(spritenum)) {
00136 sprite = GetCustomVehicleSprite(this, (Direction)(direction + 4 * IS_CUSTOM_SECONDHEAD_SPRITE(spritenum)), image_type);
00137 if (sprite != 0) return sprite;
00138
00139 spritenum = this->GetEngine()->original_image_index;
00140 }
00141
00142 assert(IsValidImageIndex<VEH_ROAD>(spritenum));
00143 sprite = direction + _roadveh_images[spritenum];
00144
00145 if (this->cargo.StoredCount() >= this->cargo_cap / 2U) sprite += _roadveh_full_adder[spritenum];
00146
00147 return sprite;
00148 }
00149
00159 void DrawRoadVehEngine(int left, int right, int preferred_x, int y, EngineID engine, PaletteID pal, EngineImageType image_type)
00160 {
00161 SpriteID sprite = GetRoadVehIcon(engine, image_type);
00162 const Sprite *real_sprite = GetSprite(sprite, ST_NORMAL);
00163 preferred_x = Clamp(preferred_x, left - UnScaleByZoom(real_sprite->x_offs, ZOOM_LVL_GUI), right - UnScaleByZoom(real_sprite->width, ZOOM_LVL_GUI) - UnScaleByZoom(real_sprite->x_offs, ZOOM_LVL_GUI));
00164 DrawSprite(sprite, pal, preferred_x, y);
00165 }
00166
00176 void GetRoadVehSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, int &yoffs, EngineImageType image_type)
00177 {
00178 const Sprite *spr = GetSprite(GetRoadVehIcon(engine, image_type), ST_NORMAL);
00179
00180 width = UnScaleByZoom(spr->width, ZOOM_LVL_GUI);
00181 height = UnScaleByZoom(spr->height, ZOOM_LVL_GUI);
00182 xoffs = UnScaleByZoom(spr->x_offs, ZOOM_LVL_GUI);
00183 yoffs = UnScaleByZoom(spr->y_offs, ZOOM_LVL_GUI);
00184 }
00185
00191 static uint GetRoadVehLength(const RoadVehicle *v)
00192 {
00193 const Engine *e = v->GetEngine();
00194 uint length = VEHICLE_LENGTH;
00195
00196 uint16 veh_len = CALLBACK_FAILED;
00197 if (e->GetGRF() != NULL && e->GetGRF()->grf_version >= 8) {
00198
00199 veh_len = GetVehicleProperty(v, PROP_ROADVEH_SHORTEN_FACTOR, CALLBACK_FAILED);
00200 if (veh_len != CALLBACK_FAILED && veh_len >= VEHICLE_LENGTH) ErrorUnknownCallbackResult(e->GetGRFID(), CBID_VEHICLE_LENGTH, veh_len);
00201 } else {
00202
00203 veh_len = GetVehicleCallback(CBID_VEHICLE_LENGTH, 0, 0, v->engine_type, v);
00204 }
00205 if (veh_len == CALLBACK_FAILED) veh_len = e->u.road.shorten_factor;
00206 if (veh_len != 0) {
00207 length -= Clamp(veh_len, 0, VEHICLE_LENGTH - 1);
00208 }
00209
00210 return length;
00211 }
00212
00219 void RoadVehUpdateCache(RoadVehicle *v, bool same_length)
00220 {
00221 assert(v->type == VEH_ROAD);
00222 assert(v->IsFrontEngine());
00223
00224 v->InvalidateNewGRFCacheOfChain();
00225
00226 v->gcache.cached_total_length = 0;
00227
00228 for (RoadVehicle *u = v; u != NULL; u = u->Next()) {
00229
00230 assert(u->First() == v);
00231
00232
00233 u->gcache.first_engine = (v == u) ? INVALID_ENGINE : v->engine_type;
00234
00235
00236 uint veh_len = GetRoadVehLength(u);
00237
00238 if (same_length && veh_len != u->gcache.cached_veh_length) VehicleLengthChanged(u);
00239
00240 u->gcache.cached_veh_length = veh_len;
00241 v->gcache.cached_total_length += u->gcache.cached_veh_length;
00242
00243
00244 u->UpdateVisualEffect();
00245
00246
00247 u->vcache.cached_cargo_age_period = GetVehicleProperty(u, PROP_ROADVEH_CARGO_AGE_PERIOD, EngInfo(u->engine_type)->cargo_age_period);
00248 }
00249
00250 uint max_speed = GetVehicleProperty(v, PROP_ROADVEH_SPEED, 0);
00251 v->vcache.cached_max_speed = (max_speed != 0) ? max_speed * 4 : RoadVehInfo(v->engine_type)->max_speed;
00252 }
00253
00263 CommandCost CmdBuildRoadVehicle(TileIndex tile, DoCommandFlag flags, const Engine *e, uint16 data, Vehicle **ret)
00264 {
00265 if (HasTileRoadType(tile, ROADTYPE_TRAM) != HasBit(e->info.misc_flags, EF_ROAD_TRAM)) return_cmd_error(STR_ERROR_DEPOT_WRONG_DEPOT_TYPE);
00266
00267 if (flags & DC_EXEC) {
00268 const RoadVehicleInfo *rvi = &e->u.road;
00269
00270 RoadVehicle *v = new RoadVehicle();
00271 *ret = v;
00272 v->direction = DiagDirToDir(GetRoadDepotDirection(tile));
00273 v->owner = _current_company;
00274
00275 v->tile = tile;
00276 int x = TileX(tile) * TILE_SIZE + TILE_SIZE / 2;
00277 int y = TileY(tile) * TILE_SIZE + TILE_SIZE / 2;
00278 v->x_pos = x;
00279 v->y_pos = y;
00280 v->z_pos = GetSlopePixelZ(x, y);
00281
00282 v->state = RVSB_IN_DEPOT;
00283 v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL;
00284
00285 v->spritenum = rvi->image_index;
00286 v->cargo_type = e->GetDefaultCargoType();
00287 v->cargo_cap = rvi->capacity;
00288 v->refit_cap = 0;
00289
00290 v->last_station_visited = INVALID_STATION;
00291 v->last_loading_station = INVALID_STATION;
00292 v->engine_type = e->index;
00293 v->gcache.first_engine = INVALID_ENGINE;
00294
00295 v->reliability = e->reliability;
00296 v->reliability_spd_dec = e->reliability_spd_dec;
00297 v->max_age = e->GetLifeLengthInDays();
00298 _new_vehicle_id = v->index;
00299
00300 v->SetServiceInterval(Company::Get(v->owner)->settings.vehicle.servint_roadveh);
00301
00302 v->date_of_last_service = _date;
00303 v->build_year = _cur_year;
00304
00305 v->cur_image = SPR_IMG_QUERY;
00306 v->random_bits = VehicleRandomBits();
00307 v->SetFrontEngine();
00308
00309 v->roadtype = HasBit(e->info.misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD;
00310 v->compatible_roadtypes = RoadTypeToRoadTypes(v->roadtype);
00311 v->gcache.cached_veh_length = VEHICLE_LENGTH;
00312
00313 if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) SetBit(v->vehicle_flags, VF_BUILT_AS_PROTOTYPE);
00314 v->SetServiceIntervalIsPercent(Company::Get(_current_company)->settings.vehicle.servint_ispercent);
00315
00316 AddArticulatedParts(v);
00317 v->InvalidateNewGRFCacheOfChain();
00318
00319
00320 for (RoadVehicle *u = v; u != NULL; u = u->Next()) {
00321 u->cargo_cap = u->GetEngine()->DetermineCapacity(u);
00322 u->refit_cap = 0;
00323 v->InvalidateNewGRFCache();
00324 u->InvalidateNewGRFCache();
00325 }
00326 RoadVehUpdateCache(v);
00327
00328 if (_settings_game.vehicle.roadveh_acceleration_model != AM_ORIGINAL) v->CargoChanged();
00329
00330 VehicleUpdatePosition(v);
00331
00332 CheckConsistencyOfArticulatedVehicle(v);
00333 }
00334
00335 return CommandCost();
00336 }
00337
00338 static FindDepotData FindClosestRoadDepot(const RoadVehicle *v, int max_distance)
00339 {
00340 if (IsRoadDepotTile(v->tile)) return FindDepotData(v->tile, 0);
00341
00342 switch (_settings_game.pf.pathfinder_for_roadvehs) {
00343 case VPF_NPF: return NPFRoadVehicleFindNearestDepot(v, max_distance);
00344 case VPF_YAPF: return YapfRoadVehicleFindNearestDepot(v, max_distance);
00345
00346 default: NOT_REACHED();
00347 }
00348 }
00349
00350 bool RoadVehicle::FindClosestDepot(TileIndex *location, DestinationID *destination, bool *reverse)
00351 {
00352 FindDepotData rfdd = FindClosestRoadDepot(this, 0);
00353 if (rfdd.best_length == UINT_MAX) return false;
00354
00355 if (location != NULL) *location = rfdd.tile;
00356 if (destination != NULL) *destination = GetDepotIndex(rfdd.tile);
00357
00358 return true;
00359 }
00360
00370 CommandCost CmdTurnRoadVeh(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00371 {
00372 RoadVehicle *v = RoadVehicle::GetIfValid(p1);
00373 if (v == NULL) return CMD_ERROR;
00374
00375 if (!v->IsPrimaryVehicle()) return CMD_ERROR;
00376
00377 CommandCost ret = CheckOwnership(v->owner);
00378 if (ret.Failed()) return ret;
00379
00380 if ((v->vehstatus & VS_STOPPED) ||
00381 (v->vehstatus & VS_CRASHED) ||
00382 v->breakdown_ctr != 0 ||
00383 v->overtaking != 0 ||
00384 v->state == RVSB_WORMHOLE ||
00385 v->IsInDepot() ||
00386 v->current_order.IsType(OT_LOADING)) {
00387 return CMD_ERROR;
00388 }
00389
00390 if (IsNormalRoadTile(v->tile) && GetDisallowedRoadDirections(v->tile) != DRD_NONE) return CMD_ERROR;
00391
00392 if (IsTileType(v->tile, MP_TUNNELBRIDGE) && DirToDiagDir(v->direction) == GetTunnelBridgeDirection(v->tile)) return CMD_ERROR;
00393
00394 if (flags & DC_EXEC) v->reverse_ctr = 180;
00395
00396 return CommandCost();
00397 }
00398
00399
00400 void RoadVehicle::MarkDirty()
00401 {
00402 for (RoadVehicle *v = this; v != NULL; v = v->Next()) {
00403 v->colourmap = PAL_NONE;
00404 v->UpdateViewport(true, false);
00405 }
00406 this->CargoChanged();
00407 }
00408
00409 void RoadVehicle::UpdateDeltaXY(Direction direction)
00410 {
00411 static const int8 _delta_xy_table[8][10] = {
00412
00413 {3, 3, -1, -1, 0, 0, -1, -1, -1, -1},
00414 {3, 7, -1, -3, 0, -1, 0, -1, 0, 0},
00415 {3, 3, -1, -1, 0, 0, 1, -1, 1, -1},
00416 {7, 3, -3, -1, -1, 0, 0, 0, 1, 0},
00417 {3, 3, -1, -1, 0, 0, 1, 1, 1, 1},
00418 {3, 7, -1, -3, 0, -1, 0, 0, 0, 1},
00419 {3, 3, -1, -1, 0, 0, -1, 1, -1, 1},
00420 {7, 3, -3, -1, -1, 0, -1, 0, 0, 0},
00421 };
00422
00423 int shorten = VEHICLE_LENGTH - this->gcache.cached_veh_length;
00424 if (!IsDiagonalDirection(direction)) shorten >>= 1;
00425
00426 const int8 *bb = _delta_xy_table[direction];
00427 this->x_bb_offs = bb[5] + bb[9] * shorten;
00428 this->y_bb_offs = bb[4] + bb[8] * shorten;;
00429 this->x_offs = bb[3];
00430 this->y_offs = bb[2];
00431 this->x_extent = bb[1] + bb[7] * shorten;
00432 this->y_extent = bb[0] + bb[6] * shorten;
00433 this->z_extent = 6;
00434 }
00435
00440 inline int RoadVehicle::GetCurrentMaxSpeed() const
00441 {
00442 int max_speed = this->vcache.cached_max_speed;
00443
00444
00445 for (const RoadVehicle *u = this; u != NULL; u = u->Next()) {
00446 if (_settings_game.vehicle.roadveh_acceleration_model == AM_REALISTIC) {
00447 if (this->state <= RVSB_TRACKDIR_MASK && IsReversingRoadTrackdir((Trackdir)this->state)) {
00448 max_speed = this->vcache.cached_max_speed / 2;
00449 break;
00450 } else if ((u->direction & 1) == 0) {
00451 max_speed = this->vcache.cached_max_speed * 3 / 4;
00452 }
00453 }
00454
00455
00456 if (u->state == RVSB_WORMHOLE && !(u->vehstatus & VS_HIDDEN)) {
00457 max_speed = min(max_speed, GetBridgeSpec(GetBridgeType(u->tile))->speed * 2);
00458 }
00459 }
00460
00461 return min(max_speed, this->current_order.max_speed * 2);
00462 }
00463
00468 static void DeleteLastRoadVeh(RoadVehicle *v)
00469 {
00470 RoadVehicle *first = v->First();
00471 Vehicle *u = v;
00472 for (; v->Next() != NULL; v = v->Next()) u = v;
00473 u->SetNext(NULL);
00474 v->last_station_visited = first->last_station_visited;
00475
00476
00477 if (IsInsideMM(v->state, RVSB_IN_ROAD_STOP, RVSB_IN_ROAD_STOP_END)) RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
00478
00479 delete v;
00480 }
00481
00482 static void RoadVehSetRandomDirection(RoadVehicle *v)
00483 {
00484 static const DirDiff delta[] = {
00485 DIRDIFF_45LEFT, DIRDIFF_SAME, DIRDIFF_SAME, DIRDIFF_45RIGHT
00486 };
00487
00488 do {
00489 uint32 r = Random();
00490
00491 v->direction = ChangeDir(v->direction, delta[r & 3]);
00492 v->UpdateViewport(true, true);
00493 } while ((v = v->Next()) != NULL);
00494 }
00495
00501 static bool RoadVehIsCrashed(RoadVehicle *v)
00502 {
00503 v->crashed_ctr++;
00504 if (v->crashed_ctr == 2) {
00505 CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
00506 } else if (v->crashed_ctr <= 45) {
00507 if ((v->tick_counter & 7) == 0) RoadVehSetRandomDirection(v);
00508 } else if (v->crashed_ctr >= 2220 && !(v->tick_counter & 0x1F)) {
00509 bool ret = v->Next() != NULL;
00510 DeleteLastRoadVeh(v);
00511 return ret;
00512 }
00513
00514 return true;
00515 }
00516
00523 static Vehicle *EnumCheckRoadVehCrashTrain(Vehicle *v, void *data)
00524 {
00525 const Vehicle *u = (Vehicle*)data;
00526
00527 return (v->type == VEH_TRAIN &&
00528 abs(v->z_pos - u->z_pos) <= 6 &&
00529 abs(v->x_pos - u->x_pos) <= 4 &&
00530 abs(v->y_pos - u->y_pos) <= 4) ? v : NULL;
00531 }
00532
00533 uint RoadVehicle::Crash(bool flooded)
00534 {
00535 uint pass = this->GroundVehicleBase::Crash(flooded);
00536 if (this->IsFrontEngine()) {
00537 pass += 1;
00538
00539
00540 if (IsInsideMM(this->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
00541 RoadStop::GetByTile(this->tile, GetRoadStopType(this->tile))->Leave(this);
00542 }
00543 }
00544 this->crashed_ctr = flooded ? 2000 : 1;
00545 return pass;
00546 }
00547
00548 static void RoadVehCrash(RoadVehicle *v)
00549 {
00550 uint pass = v->Crash();
00551
00552 AI::NewEvent(v->owner, new ScriptEventVehicleCrashed(v->index, v->tile, ScriptEventVehicleCrashed::CRASH_RV_LEVEL_CROSSING));
00553 Game::NewEvent(new ScriptEventVehicleCrashed(v->index, v->tile, ScriptEventVehicleCrashed::CRASH_RV_LEVEL_CROSSING));
00554
00555 SetDParam(0, pass);
00556 AddVehicleNewsItem(
00557 (pass == 1) ?
00558 STR_NEWS_ROAD_VEHICLE_CRASH_DRIVER : STR_NEWS_ROAD_VEHICLE_CRASH,
00559 NT_ACCIDENT,
00560 v->index
00561 );
00562
00563 ModifyStationRatingAround(v->tile, v->owner, -160, 22);
00564 if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_12_EXPLOSION, v);
00565 }
00566
00567 static bool RoadVehCheckTrainCrash(RoadVehicle *v)
00568 {
00569 for (RoadVehicle *u = v; u != NULL; u = u->Next()) {
00570 if (u->state == RVSB_WORMHOLE) continue;
00571
00572 TileIndex tile = u->tile;
00573
00574 if (!IsLevelCrossingTile(tile)) continue;
00575
00576 if (HasVehicleOnPosXY(v->x_pos, v->y_pos, u, EnumCheckRoadVehCrashTrain)) {
00577 RoadVehCrash(v);
00578 return true;
00579 }
00580 }
00581
00582 return false;
00583 }
00584
00585 TileIndex RoadVehicle::GetOrderStationLocation(StationID station)
00586 {
00587 if (station == this->last_station_visited) this->last_station_visited = INVALID_STATION;
00588
00589 const Station *st = Station::Get(station);
00590 if (!CanVehicleUseStation(this, st)) {
00591
00592 this->IncrementRealOrderIndex();
00593 return 0;
00594 }
00595
00596 return st->xy;
00597 }
00598
00599 static void StartRoadVehSound(const RoadVehicle *v)
00600 {
00601 if (!PlayVehicleSound(v, VSE_START)) {
00602 SoundID s = RoadVehInfo(v->engine_type)->sfx;
00603 if (s == SND_19_BUS_START_PULL_AWAY && (v->tick_counter & 3) == 0) {
00604 s = SND_1A_BUS_START_PULL_AWAY_WITH_HORN;
00605 }
00606 SndPlayVehicleFx(s, v);
00607 }
00608 }
00609
00610 struct RoadVehFindData {
00611 int x;
00612 int y;
00613 const Vehicle *veh;
00614 Vehicle *best;
00615 uint best_diff;
00616 Direction dir;
00617 };
00618
00619 static Vehicle *EnumCheckRoadVehClose(Vehicle *v, void *data)
00620 {
00621 static const int8 dist_x[] = { -4, -8, -4, -1, 4, 8, 4, 1 };
00622 static const int8 dist_y[] = { -4, -1, 4, 8, 4, 1, -4, -8 };
00623
00624 RoadVehFindData *rvf = (RoadVehFindData*)data;
00625
00626 short x_diff = v->x_pos - rvf->x;
00627 short y_diff = v->y_pos - rvf->y;
00628
00629 if (v->type == VEH_ROAD &&
00630 !v->IsInDepot() &&
00631 abs(v->z_pos - rvf->veh->z_pos) < 6 &&
00632 v->direction == rvf->dir &&
00633 rvf->veh->First() != v->First() &&
00634 (dist_x[v->direction] >= 0 || (x_diff > dist_x[v->direction] && x_diff <= 0)) &&
00635 (dist_x[v->direction] <= 0 || (x_diff < dist_x[v->direction] && x_diff >= 0)) &&
00636 (dist_y[v->direction] >= 0 || (y_diff > dist_y[v->direction] && y_diff <= 0)) &&
00637 (dist_y[v->direction] <= 0 || (y_diff < dist_y[v->direction] && y_diff >= 0))) {
00638 uint diff = abs(x_diff) + abs(y_diff);
00639
00640 if (diff < rvf->best_diff || (diff == rvf->best_diff && v->index < rvf->best->index)) {
00641 rvf->best = v;
00642 rvf->best_diff = diff;
00643 }
00644 }
00645
00646 return NULL;
00647 }
00648
00649 static RoadVehicle *RoadVehFindCloseTo(RoadVehicle *v, int x, int y, Direction dir, bool update_blocked_ctr = true)
00650 {
00651 RoadVehFindData rvf;
00652 RoadVehicle *front = v->First();
00653
00654 if (front->reverse_ctr != 0) return NULL;
00655
00656 rvf.x = x;
00657 rvf.y = y;
00658 rvf.dir = dir;
00659 rvf.veh = v;
00660 rvf.best_diff = UINT_MAX;
00661
00662 if (front->state == RVSB_WORMHOLE) {
00663 FindVehicleOnPos(v->tile, &rvf, EnumCheckRoadVehClose);
00664 FindVehicleOnPos(GetOtherTunnelBridgeEnd(v->tile), &rvf, EnumCheckRoadVehClose);
00665 } else {
00666 FindVehicleOnPosXY(x, y, &rvf, EnumCheckRoadVehClose);
00667 }
00668
00669
00670
00671
00672
00673 if (rvf.best_diff == UINT_MAX) {
00674 front->blocked_ctr = 0;
00675 return NULL;
00676 }
00677
00678 if (update_blocked_ctr && ++front->blocked_ctr > 1480) return NULL;
00679
00680 return RoadVehicle::From(rvf.best);
00681 }
00682
00688 static void RoadVehArrivesAt(const RoadVehicle *v, Station *st)
00689 {
00690 if (v->IsBus()) {
00691
00692 if (!(st->had_vehicle_of_type & HVOT_BUS)) {
00693 st->had_vehicle_of_type |= HVOT_BUS;
00694 SetDParam(0, st->index);
00695 AddVehicleNewsItem(
00696 v->roadtype == ROADTYPE_ROAD ? STR_NEWS_FIRST_BUS_ARRIVAL : STR_NEWS_FIRST_PASSENGER_TRAM_ARRIVAL,
00697 (v->owner == _local_company) ? NT_ARRIVAL_COMPANY : NT_ARRIVAL_OTHER,
00698 v->index,
00699 st->index
00700 );
00701 AI::NewEvent(v->owner, new ScriptEventStationFirstVehicle(st->index, v->index));
00702 Game::NewEvent(new ScriptEventStationFirstVehicle(st->index, v->index));
00703 }
00704 } else {
00705
00706 if (!(st->had_vehicle_of_type & HVOT_TRUCK)) {
00707 st->had_vehicle_of_type |= HVOT_TRUCK;
00708 SetDParam(0, st->index);
00709 AddVehicleNewsItem(
00710 v->roadtype == ROADTYPE_ROAD ? STR_NEWS_FIRST_TRUCK_ARRIVAL : STR_NEWS_FIRST_CARGO_TRAM_ARRIVAL,
00711 (v->owner == _local_company) ? NT_ARRIVAL_COMPANY : NT_ARRIVAL_OTHER,
00712 v->index,
00713 st->index
00714 );
00715 AI::NewEvent(v->owner, new ScriptEventStationFirstVehicle(st->index, v->index));
00716 Game::NewEvent(new ScriptEventStationFirstVehicle(st->index, v->index));
00717 }
00718 }
00719 }
00720
00728 int RoadVehicle::UpdateSpeed()
00729 {
00730 switch (_settings_game.vehicle.roadveh_acceleration_model) {
00731 default: NOT_REACHED();
00732 case AM_ORIGINAL:
00733 return this->DoUpdateSpeed(this->overtaking != 0 ? 512 : 256, 0, this->GetCurrentMaxSpeed());
00734
00735 case AM_REALISTIC:
00736 return this->DoUpdateSpeed(this->GetAcceleration() + (this->overtaking != 0 ? 256 : 0), this->GetAccelerationStatus() == AS_BRAKE ? 0 : 4, this->GetCurrentMaxSpeed());
00737 }
00738 }
00739
00740 static Direction RoadVehGetNewDirection(const RoadVehicle *v, int x, int y)
00741 {
00742 static const Direction _roadveh_new_dir[] = {
00743 DIR_N , DIR_NW, DIR_W , INVALID_DIR,
00744 DIR_NE, DIR_N , DIR_SW, INVALID_DIR,
00745 DIR_E , DIR_SE, DIR_S
00746 };
00747
00748 x = x - v->x_pos + 1;
00749 y = y - v->y_pos + 1;
00750
00751 if ((uint)x > 2 || (uint)y > 2) return v->direction;
00752 return _roadveh_new_dir[y * 4 + x];
00753 }
00754
00755 static Direction RoadVehGetSlidingDirection(const RoadVehicle *v, int x, int y)
00756 {
00757 Direction new_dir = RoadVehGetNewDirection(v, x, y);
00758 Direction old_dir = v->direction;
00759 DirDiff delta;
00760
00761 if (new_dir == old_dir) return old_dir;
00762 delta = (DirDifference(new_dir, old_dir) > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
00763 return ChangeDir(old_dir, delta);
00764 }
00765
00766 struct OvertakeData {
00767 const RoadVehicle *u;
00768 const RoadVehicle *v;
00769 TileIndex tile;
00770 Trackdir trackdir;
00771 };
00772
00773 static Vehicle *EnumFindVehBlockingOvertake(Vehicle *v, void *data)
00774 {
00775 const OvertakeData *od = (OvertakeData*)data;
00776
00777 return (v->type == VEH_ROAD && v->First() == v && v != od->u && v != od->v) ? v : NULL;
00778 }
00779
00786 static bool CheckRoadBlockedForOvertaking(OvertakeData *od)
00787 {
00788 TrackStatus ts = GetTileTrackStatus(od->tile, TRANSPORT_ROAD, od->v->compatible_roadtypes);
00789 TrackdirBits trackdirbits = TrackStatusToTrackdirBits(ts);
00790 TrackdirBits red_signals = TrackStatusToRedSignals(ts);
00791 TrackBits trackbits = TrackdirBitsToTrackBits(trackdirbits);
00792
00793
00794 if (!HasBit(trackdirbits, od->trackdir) || (trackbits & ~TRACK_BIT_CROSS) || (red_signals != TRACKDIR_BIT_NONE)) return true;
00795
00796
00797 return HasVehicleOnPos(od->tile, od, EnumFindVehBlockingOvertake);
00798 }
00799
00800 static void RoadVehCheckOvertake(RoadVehicle *v, RoadVehicle *u)
00801 {
00802 OvertakeData od;
00803
00804 od.v = v;
00805 od.u = u;
00806
00807 if (u->vcache.cached_max_speed >= v->vcache.cached_max_speed &&
00808 !(u->vehstatus & VS_STOPPED) &&
00809 u->cur_speed != 0) {
00810 return;
00811 }
00812
00813
00814 if (v->roadtype == ROADTYPE_TRAM) return;
00815
00816
00817 if (IsTileType(v->tile, MP_STATION) || IsTileType(u->tile, MP_STATION)) return;
00818
00819
00820 if (v->HasArticulatedPart()) return;
00821
00822
00823 if (v->direction != u->direction || !(v->direction & 1)) return;
00824
00825
00826 if (v->state >= RVSB_IN_ROAD_STOP || !IsStraightRoadTrackdir((Trackdir)(v->state & RVSB_TRACKDIR_MASK))) return;
00827
00828 od.trackdir = DiagDirToDiagTrackdir(DirToDiagDir(v->direction));
00829
00830
00831
00832
00833
00834
00835
00836 od.tile = v->tile;
00837 if (CheckRoadBlockedForOvertaking(&od)) return;
00838
00839 od.tile = v->tile + TileOffsByDiagDir(DirToDiagDir(v->direction));
00840 if (CheckRoadBlockedForOvertaking(&od)) return;
00841
00842
00843
00844 v->overtaking_ctr = (od.u->cur_speed == 0 || (od.u->vehstatus & VS_STOPPED)) ? RV_OVERTAKE_TIMEOUT / 2 : 0;
00845 v->overtaking = RVSB_DRIVE_SIDE;
00846 }
00847
00848 static void RoadZPosAffectSpeed(RoadVehicle *v, byte old_z)
00849 {
00850 if (old_z == v->z_pos || _settings_game.vehicle.roadveh_acceleration_model != AM_ORIGINAL) return;
00851
00852 if (old_z < v->z_pos) {
00853 v->cur_speed = v->cur_speed * 232 / 256;
00854 } else {
00855 uint16 spd = v->cur_speed + 2;
00856 if (spd <= v->vcache.cached_max_speed) v->cur_speed = spd;
00857 }
00858 }
00859
00860 static int PickRandomBit(uint bits)
00861 {
00862 uint i;
00863 uint num = RandomRange(CountBits(bits));
00864
00865 for (i = 0; !(bits & 1) || (int)--num >= 0; bits >>= 1, i++) {}
00866 return i;
00867 }
00868
00877 static Trackdir RoadFindPathToDest(RoadVehicle *v, TileIndex tile, DiagDirection enterdir)
00878 {
00879 #define return_track(x) { best_track = (Trackdir)x; goto found_best_track; }
00880
00881 TileIndex desttile;
00882 Trackdir best_track;
00883 bool path_found = true;
00884
00885 TrackStatus ts = GetTileTrackStatus(tile, TRANSPORT_ROAD, v->compatible_roadtypes);
00886 TrackdirBits red_signals = TrackStatusToRedSignals(ts);
00887 TrackdirBits trackdirs = TrackStatusToTrackdirBits(ts);
00888
00889 if (IsTileType(tile, MP_ROAD)) {
00890 if (IsRoadDepot(tile) && (!IsTileOwner(tile, v->owner) || GetRoadDepotDirection(tile) == enterdir || (GetRoadTypes(tile) & v->compatible_roadtypes) == 0)) {
00891
00892 trackdirs = TRACKDIR_BIT_NONE;
00893 }
00894 } else if (IsTileType(tile, MP_STATION) && IsStandardRoadStopTile(tile)) {
00895
00896
00897 if (!IsTileOwner(tile, v->owner) || GetRoadStopDir(tile) == enterdir || v->HasArticulatedPart()) {
00898
00899 trackdirs = TRACKDIR_BIT_NONE;
00900 } else {
00901
00902 RoadStopType rstype = v->IsBus() ? ROADSTOP_BUS : ROADSTOP_TRUCK;
00903
00904 if (GetRoadStopType(tile) != rstype) {
00905
00906 trackdirs = TRACKDIR_BIT_NONE;
00907 } else {
00908
00909 if (!_settings_game.pf.roadveh_queue && IsStandardRoadStopTile(tile) &&
00910 !RoadStop::GetByTile(tile, rstype)->HasFreeBay()) {
00911
00912 trackdirs = TRACKDIR_BIT_NONE;
00913 }
00914 }
00915 }
00916 }
00917
00918
00919
00920
00921
00922
00923 trackdirs &= _road_enter_dir_to_reachable_trackdirs[enterdir];
00924 if (trackdirs == TRACKDIR_BIT_NONE) {
00925
00926 return_track(_road_reverse_table[enterdir]);
00927 }
00928
00929 if (v->reverse_ctr != 0) {
00930 bool reverse = true;
00931 if (v->roadtype == ROADTYPE_TRAM) {
00932
00933
00934 RoadBits rb = GetAnyRoadBits(tile, ROADTYPE_TRAM);
00935 RoadBits straight = AxisToRoadBits(DiagDirToAxis(enterdir));
00936 reverse = ((rb & straight) == straight) ||
00937 (rb == DiagDirToRoadBits(enterdir));
00938 }
00939 if (reverse) {
00940 v->reverse_ctr = 0;
00941 if (v->tile != tile) {
00942 return_track(_road_reverse_table[enterdir]);
00943 }
00944 }
00945 }
00946
00947 desttile = v->dest_tile;
00948 if (desttile == 0) {
00949
00950 return_track(PickRandomBit(trackdirs));
00951 }
00952
00953
00954 if (KillFirstBit(trackdirs) == TRACKDIR_BIT_NONE) {
00955 return_track(FindFirstBit2x64(trackdirs));
00956 }
00957
00958 switch (_settings_game.pf.pathfinder_for_roadvehs) {
00959 case VPF_NPF: best_track = NPFRoadVehicleChooseTrack(v, tile, enterdir, trackdirs, path_found); break;
00960 case VPF_YAPF: best_track = YapfRoadVehicleChooseTrack(v, tile, enterdir, trackdirs, path_found); break;
00961
00962 default: NOT_REACHED();
00963 }
00964 v->HandlePathfindingResult(path_found);
00965
00966 found_best_track:;
00967
00968 if (HasBit(red_signals, best_track)) return INVALID_TRACKDIR;
00969
00970 return best_track;
00971 }
00972
00973 struct RoadDriveEntry {
00974 byte x, y;
00975 };
00976
00977 #include "table/roadveh_movement.h"
00978
00979 static bool RoadVehLeaveDepot(RoadVehicle *v, bool first)
00980 {
00981
00982 for (const RoadVehicle *u = v; u != NULL; u = u->Next()) {
00983 if (u->state != RVSB_IN_DEPOT || u->tile != v->tile) return false;
00984 }
00985
00986 DiagDirection dir = GetRoadDepotDirection(v->tile);
00987 v->direction = DiagDirToDir(dir);
00988
00989 Trackdir tdir = _roadveh_depot_exit_trackdir[dir];
00990 const RoadDriveEntry *rdp = _road_drive_data[v->roadtype][(_settings_game.vehicle.road_side << RVS_DRIVE_SIDE) + tdir];
00991
00992 int x = TileX(v->tile) * TILE_SIZE + (rdp[RVC_DEPOT_START_FRAME].x & 0xF);
00993 int y = TileY(v->tile) * TILE_SIZE + (rdp[RVC_DEPOT_START_FRAME].y & 0xF);
00994
00995 if (first) {
00996
00997 if (v->current_order.IsType(OT_GOTO_DEPOT) && v->tile == v->dest_tile) {
00998 VehicleEnterDepot(v);
00999 return true;
01000 }
01001
01002 if (RoadVehFindCloseTo(v, x, y, v->direction, false) != NULL) return true;
01003
01004 VehicleServiceInDepot(v);
01005
01006 StartRoadVehSound(v);
01007
01008
01009 v->cur_speed = 0;
01010 }
01011
01012 v->vehstatus &= ~VS_HIDDEN;
01013 v->state = tdir;
01014 v->frame = RVC_DEPOT_START_FRAME;
01015
01016 v->x_pos = x;
01017 v->y_pos = y;
01018 VehicleUpdatePosition(v);
01019 v->UpdateInclination(true, true);
01020
01021 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01022
01023 return true;
01024 }
01025
01026 static Trackdir FollowPreviousRoadVehicle(const RoadVehicle *v, const RoadVehicle *prev, TileIndex tile, DiagDirection entry_dir, bool already_reversed)
01027 {
01028 if (prev->tile == v->tile && !already_reversed) {
01029
01030
01031 return _road_reverse_table[entry_dir];
01032 }
01033
01034 byte prev_state = prev->state;
01035 Trackdir dir;
01036
01037 if (prev_state == RVSB_WORMHOLE || prev_state == RVSB_IN_DEPOT) {
01038 DiagDirection diag_dir = INVALID_DIAGDIR;
01039
01040 if (IsTileType(tile, MP_TUNNELBRIDGE)) {
01041 diag_dir = GetTunnelBridgeDirection(tile);
01042 } else if (IsRoadDepotTile(tile)) {
01043 diag_dir = ReverseDiagDir(GetRoadDepotDirection(tile));
01044 }
01045
01046 if (diag_dir == INVALID_DIAGDIR) return INVALID_TRACKDIR;
01047 dir = DiagDirToDiagTrackdir(diag_dir);
01048 } else {
01049 if (already_reversed && prev->tile != tile) {
01050
01051
01052
01053
01054
01055
01056
01057
01058
01059
01060
01061
01062
01063
01064
01065 static const Trackdir reversed_turn_lookup[2][DIAGDIR_END] = {
01066 { TRACKDIR_UPPER_W, TRACKDIR_RIGHT_N, TRACKDIR_LEFT_N, TRACKDIR_UPPER_E },
01067 { TRACKDIR_RIGHT_S, TRACKDIR_LOWER_W, TRACKDIR_LOWER_E, TRACKDIR_LEFT_S }};
01068 dir = reversed_turn_lookup[prev->tile < tile ? 0 : 1][ReverseDiagDir(entry_dir)];
01069 } else if (HasBit(prev_state, RVS_IN_DT_ROAD_STOP)) {
01070 dir = (Trackdir)(prev_state & RVSB_ROAD_STOP_TRACKDIR_MASK);
01071 } else if (prev_state < TRACKDIR_END) {
01072 dir = (Trackdir)prev_state;
01073 } else {
01074 return INVALID_TRACKDIR;
01075 }
01076 }
01077
01078
01079 static const RoadBits required_roadbits[] = {
01080 ROAD_X, ROAD_Y, ROAD_NW | ROAD_NE, ROAD_SW | ROAD_SE,
01081 ROAD_NW | ROAD_SW, ROAD_NE | ROAD_SE, ROAD_X, ROAD_Y
01082 };
01083 RoadBits required = required_roadbits[dir & 0x07];
01084
01085 if ((required & GetAnyRoadBits(tile, v->roadtype, true)) == ROAD_NONE) {
01086 dir = INVALID_TRACKDIR;
01087 }
01088
01089 return dir;
01090 }
01091
01099 static bool CanBuildTramTrackOnTile(CompanyID c, TileIndex t, RoadBits r)
01100 {
01101
01102 Backup<CompanyByte> cur_company(_current_company, c, FILE_LINE);
01103
01104 CommandCost ret = DoCommand(t, ROADTYPE_TRAM << 4 | r, 0, DC_NO_WATER, CMD_BUILD_ROAD);
01105
01106 cur_company.Restore();
01107 return ret.Succeeded();
01108 }
01109
01110 bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev)
01111 {
01112 if (v->overtaking != 0) {
01113 if (IsTileType(v->tile, MP_STATION)) {
01114
01115 v->overtaking = 0;
01116 } else if (++v->overtaking_ctr >= RV_OVERTAKE_TIMEOUT) {
01117
01118
01119
01120 if (v->state < RVSB_IN_ROAD_STOP && IsStraightRoadTrackdir((Trackdir)v->state)) {
01121 v->overtaking = 0;
01122 }
01123 }
01124 }
01125
01126
01127
01128
01129 if (v->IsInDepot()) return true;
01130
01131 if (v->state == RVSB_WORMHOLE) {
01132
01133 GetNewVehiclePosResult gp = GetNewVehiclePos(v);
01134
01135 if (v->IsFrontEngine()) {
01136 const Vehicle *u = RoadVehFindCloseTo(v, gp.x, gp.y, v->direction);
01137 if (u != NULL) {
01138 v->cur_speed = u->First()->cur_speed;
01139 return false;
01140 }
01141 }
01142
01143 if (IsTileType(gp.new_tile, MP_TUNNELBRIDGE) && HasBit(VehicleEnterTile(v, gp.new_tile, gp.x, gp.y), VETS_ENTERED_WORMHOLE)) {
01144
01145 v->x_pos = gp.x;
01146 v->y_pos = gp.y;
01147 VehicleUpdatePosition(v);
01148 v->UpdateInclination(true, true);
01149 return true;
01150 }
01151
01152 v->x_pos = gp.x;
01153 v->y_pos = gp.y;
01154 VehicleUpdatePosition(v);
01155 if ((v->vehstatus & VS_HIDDEN) == 0) VehicleUpdateViewport(v, true);
01156 return true;
01157 }
01158
01159
01160
01161
01162 RoadDriveEntry rd = _road_drive_data[v->roadtype][(
01163 (HasBit(v->state, RVS_IN_DT_ROAD_STOP) ? v->state & RVSB_ROAD_STOP_TRACKDIR_MASK : v->state) +
01164 (_settings_game.vehicle.road_side << RVS_DRIVE_SIDE)) ^ v->overtaking][v->frame + 1];
01165
01166 if (rd.x & RDE_NEXT_TILE) {
01167 TileIndex tile = v->tile + TileOffsByDiagDir((DiagDirection)(rd.x & 3));
01168 Trackdir dir;
01169
01170 if (v->IsFrontEngine()) {
01171
01172 dir = RoadFindPathToDest(v, tile, (DiagDirection)(rd.x & 3));
01173 } else {
01174 dir = FollowPreviousRoadVehicle(v, prev, tile, (DiagDirection)(rd.x & 3), false);
01175 }
01176
01177 if (dir == INVALID_TRACKDIR) {
01178 if (!v->IsFrontEngine()) error("Disconnecting road vehicle.");
01179 v->cur_speed = 0;
01180 return false;
01181 }
01182
01183 again:
01184 uint start_frame = RVC_DEFAULT_START_FRAME;
01185 if (IsReversingRoadTrackdir(dir)) {
01186
01187 v->overtaking = 0;
01188
01189
01190 if (v->roadtype == ROADTYPE_TRAM) {
01191
01192
01193 RoadBits needed;
01194 switch (dir) {
01195 default: NOT_REACHED();
01196 case TRACKDIR_RVREV_NE: needed = ROAD_SW; break;
01197 case TRACKDIR_RVREV_SE: needed = ROAD_NW; break;
01198 case TRACKDIR_RVREV_SW: needed = ROAD_NE; break;
01199 case TRACKDIR_RVREV_NW: needed = ROAD_SE; break;
01200 }
01201 if ((v->Previous() != NULL && v->Previous()->tile == tile) ||
01202 (v->IsFrontEngine() && IsNormalRoadTile(tile) && !HasRoadWorks(tile) &&
01203 (needed & GetRoadBits(tile, ROADTYPE_TRAM)) != ROAD_NONE)) {
01204
01205
01206
01207
01208
01209
01210
01211
01212
01213
01214 } else if (!v->IsFrontEngine() || !CanBuildTramTrackOnTile(v->owner, tile, needed) || ((~needed & GetAnyRoadBits(v->tile, ROADTYPE_TRAM, false)) == ROAD_NONE)) {
01215
01216
01217
01218
01219
01220
01221
01222
01223
01224
01225
01226 tile = v->tile;
01227 start_frame = RVC_TURN_AROUND_START_FRAME_SHORT_TRAM;
01228 } else {
01229
01230 v->cur_speed = 0;
01231 return false;
01232 }
01233 } else if (IsNormalRoadTile(v->tile) && GetDisallowedRoadDirections(v->tile) != DRD_NONE) {
01234 v->cur_speed = 0;
01235 return false;
01236 } else {
01237 tile = v->tile;
01238 }
01239 }
01240
01241
01242 const RoadDriveEntry *rdp = _road_drive_data[v->roadtype][(dir + (_settings_game.vehicle.road_side << RVS_DRIVE_SIDE)) ^ v->overtaking];
01243
01244 int x = TileX(tile) * TILE_SIZE + rdp[start_frame].x;
01245 int y = TileY(tile) * TILE_SIZE + rdp[start_frame].y;
01246
01247 Direction new_dir = RoadVehGetSlidingDirection(v, x, y);
01248 if (v->IsFrontEngine()) {
01249 Vehicle *u = RoadVehFindCloseTo(v, x, y, new_dir);
01250 if (u != NULL) {
01251 v->cur_speed = u->First()->cur_speed;
01252 return false;
01253 }
01254 }
01255
01256 uint32 r = VehicleEnterTile(v, tile, x, y);
01257 if (HasBit(r, VETS_CANNOT_ENTER)) {
01258 if (!IsTileType(tile, MP_TUNNELBRIDGE)) {
01259 v->cur_speed = 0;
01260 return false;
01261 }
01262
01263 dir = _road_reverse_table[rd.x & 3];
01264 goto again;
01265 }
01266
01267 if (IsInsideMM(v->state, RVSB_IN_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END) && IsTileType(v->tile, MP_STATION)) {
01268 if (IsReversingRoadTrackdir(dir) && IsInsideMM(v->state, RVSB_IN_ROAD_STOP, RVSB_IN_ROAD_STOP_END)) {
01269
01270
01271 v->cur_speed = 0;
01272 return false;
01273 }
01274
01275
01276
01277
01278
01279
01280
01281
01282 if (IsDriveThroughStopTile(v->tile) &&
01283 RoadStop::IsDriveThroughRoadStopContinuation(v->tile, tile) &&
01284 v->tile != tile) {
01285
01286 dir = (Trackdir)v->state;
01287 } else if (IsRoadStop(v->tile)) {
01288
01289 RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
01290 }
01291 }
01292
01293 if (!HasBit(r, VETS_ENTERED_WORMHOLE)) {
01294 v->tile = tile;
01295 v->state = (byte)dir;
01296 v->frame = start_frame;
01297 }
01298 if (new_dir != v->direction) {
01299 v->direction = new_dir;
01300 if (_settings_game.vehicle.roadveh_acceleration_model == AM_ORIGINAL) v->cur_speed -= v->cur_speed >> 2;
01301 }
01302 v->x_pos = x;
01303 v->y_pos = y;
01304 VehicleUpdatePosition(v);
01305 RoadZPosAffectSpeed(v, v->UpdateInclination(true, true));
01306 return true;
01307 }
01308
01309 if (rd.x & RDE_TURNED) {
01310
01311 Trackdir dir;
01312 uint turn_around_start_frame = RVC_TURN_AROUND_START_FRAME;
01313
01314 if (v->roadtype == ROADTYPE_TRAM && !IsRoadDepotTile(v->tile) && HasExactlyOneBit(GetAnyRoadBits(v->tile, ROADTYPE_TRAM, true))) {
01315
01316
01317
01318
01319
01320
01321
01322
01323
01324 turn_around_start_frame = RVC_START_FRAME_AFTER_LONG_TRAM;
01325 switch (rd.x & 0x3) {
01326 default: NOT_REACHED();
01327 case DIAGDIR_NW: dir = TRACKDIR_RVREV_SE; break;
01328 case DIAGDIR_NE: dir = TRACKDIR_RVREV_SW; break;
01329 case DIAGDIR_SE: dir = TRACKDIR_RVREV_NW; break;
01330 case DIAGDIR_SW: dir = TRACKDIR_RVREV_NE; break;
01331 }
01332 } else {
01333 if (v->IsFrontEngine()) {
01334
01335 dir = RoadFindPathToDest(v, v->tile, (DiagDirection)(rd.x & 3));
01336 } else {
01337 dir = FollowPreviousRoadVehicle(v, prev, v->tile, (DiagDirection)(rd.x & 3), true);
01338 }
01339 }
01340
01341 if (dir == INVALID_TRACKDIR) {
01342 v->cur_speed = 0;
01343 return false;
01344 }
01345
01346 const RoadDriveEntry *rdp = _road_drive_data[v->roadtype][(_settings_game.vehicle.road_side << RVS_DRIVE_SIDE) + dir];
01347
01348 int x = TileX(v->tile) * TILE_SIZE + rdp[turn_around_start_frame].x;
01349 int y = TileY(v->tile) * TILE_SIZE + rdp[turn_around_start_frame].y;
01350
01351 Direction new_dir = RoadVehGetSlidingDirection(v, x, y);
01352 if (v->IsFrontEngine() && RoadVehFindCloseTo(v, x, y, new_dir) != NULL) return false;
01353
01354 uint32 r = VehicleEnterTile(v, v->tile, x, y);
01355 if (HasBit(r, VETS_CANNOT_ENTER)) {
01356 v->cur_speed = 0;
01357 return false;
01358 }
01359
01360 v->state = dir;
01361 v->frame = turn_around_start_frame;
01362
01363 if (new_dir != v->direction) {
01364 v->direction = new_dir;
01365 if (_settings_game.vehicle.roadveh_acceleration_model == AM_ORIGINAL) v->cur_speed -= v->cur_speed >> 2;
01366 }
01367
01368 v->x_pos = x;
01369 v->y_pos = y;
01370 VehicleUpdatePosition(v);
01371 RoadZPosAffectSpeed(v, v->UpdateInclination(true, true));
01372 return true;
01373 }
01374
01375
01376
01377
01378 if (v->Next() != NULL && IsRoadDepotTile(v->tile)) {
01379 if (v->frame == v->gcache.cached_veh_length + RVC_DEPOT_START_FRAME) {
01380 RoadVehLeaveDepot(v->Next(), false);
01381 }
01382 }
01383
01384
01385 int x = (v->x_pos & ~15) + (rd.x & 15);
01386 int y = (v->y_pos & ~15) + (rd.y & 15);
01387
01388 Direction new_dir = RoadVehGetSlidingDirection(v, x, y);
01389
01390 if (v->IsFrontEngine() && !IsInsideMM(v->state, RVSB_IN_ROAD_STOP, RVSB_IN_ROAD_STOP_END)) {
01391
01392
01393 RoadVehicle *u = RoadVehFindCloseTo(v, x, y, new_dir);
01394
01395 if (u != NULL) {
01396 u = u->First();
01397
01398 if (v->overtaking == 0) RoadVehCheckOvertake(v, u);
01399 if (v->overtaking == 0) v->cur_speed = u->cur_speed;
01400
01401
01402 if (v->cur_speed == 0 && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END) &&
01403 v->current_order.ShouldStopAtStation(v, GetStationIndex(v->tile)) &&
01404 v->owner == GetTileOwner(v->tile) && !v->current_order.IsType(OT_LEAVESTATION) &&
01405 GetRoadStopType(v->tile) == (v->IsBus() ? ROADSTOP_BUS : ROADSTOP_TRUCK)) {
01406 Station *st = Station::GetByTile(v->tile);
01407 v->last_station_visited = st->index;
01408 RoadVehArrivesAt(v, st);
01409 v->BeginLoading();
01410 }
01411 return false;
01412 }
01413 }
01414
01415 Direction old_dir = v->direction;
01416 if (new_dir != old_dir) {
01417 v->direction = new_dir;
01418 if (_settings_game.vehicle.roadveh_acceleration_model == AM_ORIGINAL) v->cur_speed -= v->cur_speed >> 2;
01419
01420
01421
01422
01423
01424 v->UpdateInclination(false, true);
01425 return true;
01426 }
01427
01428
01429
01430
01431
01432
01433 if (v->IsFrontEngine() && ((IsInsideMM(v->state, RVSB_IN_ROAD_STOP, RVSB_IN_ROAD_STOP_END) &&
01434 _road_stop_stop_frame[v->state - RVSB_IN_ROAD_STOP + (_settings_game.vehicle.road_side << RVS_DRIVE_SIDE)] == v->frame) ||
01435 (IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END) &&
01436 v->current_order.ShouldStopAtStation(v, GetStationIndex(v->tile)) &&
01437 v->owner == GetTileOwner(v->tile) &&
01438 GetRoadStopType(v->tile) == (v->IsBus() ? ROADSTOP_BUS : ROADSTOP_TRUCK) &&
01439 v->frame == RVC_DRIVE_THROUGH_STOP_FRAME))) {
01440
01441 RoadStop *rs = RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile));
01442 Station *st = Station::GetByTile(v->tile);
01443
01444
01445
01446
01447 if (!HasBit(v->state, RVS_ENTERED_STOP)) {
01448
01449
01450 if (IsDriveThroughStopTile(v->tile)) {
01451 TileIndex next_tile = TILE_ADD(v->tile, TileOffsByDir(v->direction));
01452
01453
01454 if (RoadStop::IsDriveThroughRoadStopContinuation(v->tile, next_tile) && (GetRoadTypes(next_tile) & v->compatible_roadtypes) != 0) {
01455 v->frame++;
01456 v->x_pos = x;
01457 v->y_pos = y;
01458 VehicleUpdatePosition(v);
01459 RoadZPosAffectSpeed(v, v->UpdateInclination(true, false));
01460 return true;
01461 }
01462 }
01463
01464 rs->SetEntranceBusy(false);
01465 SetBit(v->state, RVS_ENTERED_STOP);
01466
01467 v->last_station_visited = st->index;
01468
01469 if (IsDriveThroughStopTile(v->tile) || (v->current_order.IsType(OT_GOTO_STATION) && v->current_order.GetDestination() == st->index)) {
01470 RoadVehArrivesAt(v, st);
01471 v->BeginLoading();
01472 return false;
01473 }
01474 } else {
01475
01476 if (rs->IsEntranceBusy()) {
01477
01478 v->cur_speed = 0;
01479 return false;
01480 }
01481 if (v->current_order.IsType(OT_LEAVESTATION)) v->current_order.Free();
01482 }
01483
01484 if (IsStandardRoadStopTile(v->tile)) rs->SetEntranceBusy(true);
01485
01486 StartRoadVehSound(v);
01487 SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
01488 }
01489
01490
01491
01492 uint32 r = VehicleEnterTile(v, v->tile, x, y);
01493 if (HasBit(r, VETS_CANNOT_ENTER)) {
01494 v->cur_speed = 0;
01495 return false;
01496 }
01497
01498 if (v->current_order.IsType(OT_LEAVESTATION) && IsDriveThroughStopTile(v->tile)) {
01499 v->current_order.Free();
01500 }
01501
01502
01503
01504 if (!HasBit(r, VETS_ENTERED_WORMHOLE)) v->frame++;
01505 v->x_pos = x;
01506 v->y_pos = y;
01507 VehicleUpdatePosition(v);
01508 RoadZPosAffectSpeed(v, v->UpdateInclination(false, true));
01509 return true;
01510 }
01511
01512 static bool RoadVehController(RoadVehicle *v)
01513 {
01514
01515 v->current_order_time++;
01516 if (v->reverse_ctr != 0) v->reverse_ctr--;
01517
01518
01519 if (v->vehstatus & VS_CRASHED || RoadVehCheckTrainCrash(v)) {
01520 return RoadVehIsCrashed(v);
01521 }
01522
01523
01524 if (v->HandleBreakdown()) return true;
01525 if (v->vehstatus & VS_STOPPED) return true;
01526
01527 ProcessOrders(v);
01528 v->HandleLoading();
01529
01530 if (v->current_order.IsType(OT_LOADING)) return true;
01531
01532 if (v->IsInDepot() && RoadVehLeaveDepot(v, true)) return true;
01533
01534 v->ShowVisualEffect();
01535
01536
01537 int j = v->UpdateSpeed();
01538
01539 int adv_spd = v->GetAdvanceDistance();
01540 bool blocked = false;
01541 while (j >= adv_spd) {
01542 j -= adv_spd;
01543
01544 RoadVehicle *u = v;
01545 for (RoadVehicle *prev = NULL; u != NULL; prev = u, u = u->Next()) {
01546 if (!IndividualRoadVehicleController(u, prev)) {
01547 blocked = true;
01548 break;
01549 }
01550 }
01551 if (blocked) break;
01552
01553
01554 adv_spd = v->GetAdvanceDistance();
01555
01556
01557 if (j >= adv_spd && RoadVehCheckTrainCrash(v)) break;
01558 }
01559
01560 v->SetLastSpeed();
01561
01562 for (RoadVehicle *u = v; u != NULL; u = u->Next()) {
01563 if ((u->vehstatus & VS_HIDDEN) != 0) continue;
01564
01565 u->UpdateViewport(false, false);
01566 }
01567
01568
01569
01570
01571 if (v->progress == 0) v->progress = blocked ? adv_spd - 1 : j;
01572
01573 return true;
01574 }
01575
01576 Money RoadVehicle::GetRunningCost() const
01577 {
01578 const Engine *e = this->GetEngine();
01579 if (e->u.road.running_cost_class == INVALID_PRICE) return 0;
01580
01581 uint cost_factor = GetVehicleProperty(this, PROP_ROADVEH_RUNNING_COST_FACTOR, e->u.road.running_cost);
01582 if (cost_factor == 0) return 0;
01583
01584 return GetPrice(e->u.road.running_cost_class, cost_factor, e->GetGRF());
01585 }
01586
01587 bool RoadVehicle::Tick()
01588 {
01589 this->tick_counter++;
01590
01591 if (this->IsFrontEngine()) {
01592 if (!(this->vehstatus & VS_STOPPED)) this->running_ticks++;
01593 return RoadVehController(this);
01594 }
01595
01596 return true;
01597 }
01598
01599 static void CheckIfRoadVehNeedsService(RoadVehicle *v)
01600 {
01601
01602 if (Company::Get(v->owner)->settings.vehicle.servint_roadveh == 0 || !v->NeedsAutomaticServicing()) return;
01603 if (v->IsChainInDepot()) {
01604 VehicleServiceInDepot(v);
01605 return;
01606 }
01607
01608 uint max_penalty;
01609 switch (_settings_game.pf.pathfinder_for_roadvehs) {
01610 case VPF_NPF: max_penalty = _settings_game.pf.npf.maximum_go_to_depot_penalty; break;
01611 case VPF_YAPF: max_penalty = _settings_game.pf.yapf.maximum_go_to_depot_penalty; break;
01612 default: NOT_REACHED();
01613 }
01614
01615 FindDepotData rfdd = FindClosestRoadDepot(v, max_penalty);
01616
01617 if (rfdd.best_length == UINT_MAX || rfdd.best_length > max_penalty) {
01618 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01619
01620
01621
01622 v->current_order.MakeDummy();
01623 SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
01624 }
01625 return;
01626 }
01627
01628 DepotID depot = GetDepotIndex(rfdd.tile);
01629
01630 if (v->current_order.IsType(OT_GOTO_DEPOT) &&
01631 v->current_order.GetNonStopType() & ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS &&
01632 !Chance16(1, 20)) {
01633 return;
01634 }
01635
01636 SetBit(v->gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
01637 v->current_order.MakeGoToDepot(depot, ODTFB_SERVICE);
01638 v->dest_tile = rfdd.tile;
01639 SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
01640 }
01641
01642 void RoadVehicle::OnNewDay()
01643 {
01644 AgeVehicle(this);
01645
01646 if (!this->IsFrontEngine()) return;
01647
01648 if ((++this->day_counter & 7) == 0) DecreaseVehicleValue(this);
01649 if (this->blocked_ctr == 0) CheckVehicleBreakdown(this);
01650
01651 CheckIfRoadVehNeedsService(this);
01652
01653 CheckOrders(this);
01654
01655 if (this->running_ticks == 0) return;
01656
01657 CommandCost cost(EXPENSES_ROADVEH_RUN, this->GetRunningCost() * this->running_ticks / (DAYS_IN_YEAR * DAY_TICKS));
01658
01659 this->profit_this_year -= cost.GetCost();
01660 this->running_ticks = 0;
01661
01662 SubtractMoneyFromCompanyFract(this->owner, cost);
01663
01664 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01665 SetWindowClassesDirty(WC_ROADVEH_LIST);
01666 }
01667
01668 Trackdir RoadVehicle::GetVehicleTrackdir() const
01669 {
01670 if (this->vehstatus & VS_CRASHED) return INVALID_TRACKDIR;
01671
01672 if (this->IsInDepot()) {
01673
01674 return DiagDirToDiagTrackdir(GetRoadDepotDirection(this->tile));
01675 }
01676
01677 if (IsStandardRoadStopTile(this->tile)) {
01678
01679 return DiagDirToDiagTrackdir(GetRoadStopDir(this->tile));
01680 }
01681
01682
01683 if (this->state > RVSB_TRACKDIR_MASK) return DiagDirToDiagTrackdir(DirToDiagDir(this->direction));
01684
01685
01686
01687 return (Trackdir)((IsReversingRoadTrackdir((Trackdir)this->state)) ? (this->state - 6) : this->state);
01688 }