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