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