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