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