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