00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include "error.h"
00014 #include "roadveh.h"
00015 #include "ship.h"
00016 #include "spritecache.h"
00017 #include "timetable.h"
00018 #include "viewport_func.h"
00019 #include "news_func.h"
00020 #include "command_func.h"
00021 #include "company_func.h"
00022 #include "train.h"
00023 #include "aircraft.h"
00024 #include "newgrf_debug.h"
00025 #include "newgrf_sound.h"
00026 #include "newgrf_station.h"
00027 #include "group_gui.h"
00028 #include "strings_func.h"
00029 #include "zoom_func.h"
00030 #include "date_func.h"
00031 #include "vehicle_func.h"
00032 #include "autoreplace_func.h"
00033 #include "autoreplace_gui.h"
00034 #include "station_base.h"
00035 #include "ai/ai.hpp"
00036 #include "depot_func.h"
00037 #include "network/network.h"
00038 #include "core/pool_func.hpp"
00039 #include "economy_base.h"
00040 #include "articulated_vehicles.h"
00041 #include "roadstop_base.h"
00042 #include "core/random_func.hpp"
00043 #include "core/backup_type.hpp"
00044 #include "order_backup.h"
00045 #include "sound_func.h"
00046 #include "effectvehicle_func.h"
00047 #include "effectvehicle_base.h"
00048 #include "vehiclelist.h"
00049 #include "bridge_map.h"
00050 #include "tunnel_map.h"
00051 #include "depot_map.h"
00052 #include "gamelog.h"
00053
00054 #include "table/strings.h"
00055
00056 #define GEN_HASH(x, y) ((GB((y), 6 + ZOOM_LVL_SHIFT, 6) << 6) + GB((x), 7 + ZOOM_LVL_SHIFT, 6))
00057
00058 VehicleID _new_vehicle_id;
00059 uint16 _returned_refit_capacity;
00060 uint16 _returned_mail_refit_capacity;
00061
00062
00064 VehiclePool _vehicle_pool("Vehicle");
00065 INSTANTIATE_POOL_METHODS(Vehicle)
00066
00067
00072 bool Vehicle::NeedsAutorenewing(const Company *c) const
00073 {
00074
00075
00076
00077
00078 assert(c == Company::Get(this->owner));
00079
00080 if (!c->settings.engine_renew) return false;
00081 if (this->age - this->max_age < (c->settings.engine_renew_months * 30)) return false;
00082
00083
00084 if (this->type == VEH_TRAIN && !Train::From(this)->IsEngine()) return false;
00085
00086 return true;
00087 }
00088
00089 void VehicleServiceInDepot(Vehicle *v)
00090 {
00091 v->date_of_last_service = _date;
00092 v->breakdowns_since_last_service = 0;
00093 v->reliability = v->GetEngine()->reliability;
00094 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
00095 }
00096
00103 bool Vehicle::NeedsServicing() const
00104 {
00105
00106
00107 if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
00108
00109
00110 const Company *c = Company::Get(this->owner);
00111 if (c->settings.vehicle.servint_ispercent ?
00112 (this->reliability >= this->GetEngine()->reliability * (100 - this->service_interval) / 100) :
00113 (this->date_of_last_service + this->service_interval >= _date)) {
00114 return false;
00115 }
00116
00117
00118
00119 if (!_settings_game.order.no_servicing_if_no_breakdowns ||
00120 _settings_game.difficulty.vehicle_breakdowns != 0) {
00121 return true;
00122 }
00123
00124
00125
00126
00127 bool pending_replace = false;
00128 Money needed_money = c->settings.engine_renew_money;
00129 if (needed_money > c->money) return false;
00130
00131 for (const Vehicle *v = this; v != NULL; v = (v->type == VEH_TRAIN) ? Train::From(v)->GetNextUnit() : NULL) {
00132 EngineID new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id);
00133
00134
00135 if (new_engine == INVALID_ENGINE || !HasBit(Engine::Get(new_engine)->company_avail, v->owner)) continue;
00136
00137
00138 uint32 available_cargo_types, union_mask;
00139 GetArticulatedRefitMasks(new_engine, true, &union_mask, &available_cargo_types);
00140
00141 if (union_mask != 0) {
00142 CargoID cargo_type;
00143
00144 if (IsArticulatedVehicleCarryingDifferentCargoes(v, &cargo_type)) continue;
00145
00146
00147 if (cargo_type != CT_INVALID) {
00148
00149 if (!HasBit(available_cargo_types, cargo_type)) continue;
00150 }
00151 }
00152
00153
00154
00155 pending_replace = true;
00156 needed_money += 2 * Engine::Get(new_engine)->GetCost();
00157 if (needed_money > c->money) return false;
00158 }
00159
00160 return pending_replace;
00161 }
00162
00168 bool Vehicle::NeedsAutomaticServicing() const
00169 {
00170 if (this->HasDepotOrder()) return false;
00171 if (this->current_order.IsType(OT_LOADING)) return false;
00172 if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
00173 return NeedsServicing();
00174 }
00175
00176 uint Vehicle::Crash(bool flooded)
00177 {
00178 assert((this->vehstatus & VS_CRASHED) == 0);
00179 assert(this->Previous() == NULL);
00180
00181 uint pass = 0;
00182
00183 if (this->IsPrimaryVehicle()) this->vehstatus |= VS_STOPPED;
00184
00185 for (Vehicle *v = this; v != NULL; v = v->Next()) {
00186 if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo.Count();
00187 v->vehstatus |= VS_CRASHED;
00188 MarkSingleVehicleDirty(v);
00189 }
00190
00191
00192 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00193 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
00194 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
00195 SetWindowDirty(WC_VEHICLE_DEPOT, this->tile);
00196
00197 return pass;
00198 }
00199
00200
00209 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
00210 {
00211 const Engine *e = Engine::Get(engine);
00212 GRFConfig *grfconfig = GetGRFConfig(e->GetGRFID());
00213
00214 if (!HasBit(grfconfig->grf_bugs, bug_type)) {
00215 SetBit(grfconfig->grf_bugs, bug_type);
00216 SetDParamStr(0, grfconfig->GetName());
00217 SetDParam(1, engine);
00218 ShowErrorMessage(part1, part2, WL_CRITICAL);
00219 if (!_networking) DoCommand(0, critical ? PM_PAUSED_ERROR : PM_PAUSED_NORMAL, 1, DC_EXEC, CMD_PAUSE);
00220 }
00221
00222
00223 char buffer[512];
00224
00225 SetDParamStr(0, grfconfig->GetName());
00226 GetString(buffer, part1, lastof(buffer));
00227 DEBUG(grf, 0, "%s", buffer + 3);
00228
00229 SetDParam(1, engine);
00230 GetString(buffer, part2, lastof(buffer));
00231 DEBUG(grf, 0, "%s", buffer + 3);
00232 }
00233
00239 void VehicleLengthChanged(const Vehicle *u)
00240 {
00241
00242 const Engine *engine = u->GetEngine();
00243 uint32 grfid = engine->grf_prop.grffile->grfid;
00244 GRFConfig *grfconfig = GetGRFConfig(grfid);
00245 if (GamelogGRFBugReverse(grfid, engine->grf_prop.local_id) || !HasBit(grfconfig->grf_bugs, GBUG_VEH_LENGTH)) {
00246 ShowNewGrfVehicleError(u->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_VEHICLE_LENGTH, GBUG_VEH_LENGTH, true);
00247 }
00248 }
00249
00254 Vehicle::Vehicle(VehicleType type)
00255 {
00256 this->type = type;
00257 this->coord.left = INVALID_COORD;
00258 this->group_id = DEFAULT_GROUP;
00259 this->fill_percent_te_id = INVALID_TE_ID;
00260 this->first = this;
00261 this->colourmap = PAL_NONE;
00262 this->cargo_age_counter = 1;
00263 }
00264
00269 byte VehicleRandomBits()
00270 {
00271 return GB(Random(), 0, 8);
00272 }
00273
00274
00275
00276 const int HASH_BITS = 7;
00277 const int HASH_SIZE = 1 << HASH_BITS;
00278 const int HASH_MASK = HASH_SIZE - 1;
00279 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
00280 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
00281
00282
00283
00284 const int HASH_RES = 0;
00285
00286 static Vehicle *_vehicle_tile_hash[TOTAL_HASH_SIZE];
00287
00288 static Vehicle *VehicleFromTileHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
00289 {
00290 for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
00291 for (int x = xl; ; x = (x + 1) & HASH_MASK) {
00292 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
00293 for (; v != NULL; v = v->hash_tile_next) {
00294 Vehicle *a = proc(v, data);
00295 if (find_first && a != NULL) return a;
00296 }
00297 if (x == xu) break;
00298 }
00299 if (y == yu) break;
00300 }
00301
00302 return NULL;
00303 }
00304
00305
00317 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
00318 {
00319 const int COLL_DIST = 6;
00320
00321
00322 int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00323 int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00324 int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00325 int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00326
00327 return VehicleFromTileHash(xl, yl, xu, yu, data, proc, find_first);
00328 }
00329
00344 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00345 {
00346 VehicleFromPosXY(x, y, data, proc, false);
00347 }
00348
00360 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00361 {
00362 return VehicleFromPosXY(x, y, data, proc, true) != NULL;
00363 }
00364
00375 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
00376 {
00377 int x = GB(TileX(tile), HASH_RES, HASH_BITS);
00378 int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
00379
00380 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
00381 for (; v != NULL; v = v->hash_tile_next) {
00382 if (v->tile != tile) continue;
00383
00384 Vehicle *a = proc(v, data);
00385 if (find_first && a != NULL) return a;
00386 }
00387
00388 return NULL;
00389 }
00390
00404 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00405 {
00406 VehicleFromPos(tile, data, proc, false);
00407 }
00408
00419 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00420 {
00421 return VehicleFromPos(tile, data, proc, true) != NULL;
00422 }
00423
00430 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
00431 {
00432 int z = *(int*)data;
00433
00434 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
00435 if (v->z_pos > z) return NULL;
00436
00437 return v;
00438 }
00439
00445 CommandCost EnsureNoVehicleOnGround(TileIndex tile)
00446 {
00447 int z = GetTileMaxPixelZ(tile);
00448
00449
00450
00451
00452
00453 Vehicle *v = VehicleFromPos(tile, &z, &EnsureNoVehicleProcZ, true);
00454 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00455 return CommandCost();
00456 }
00457
00459 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
00460 {
00461 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
00462 if (v == (const Vehicle *)data) return NULL;
00463
00464 return v;
00465 }
00466
00474 CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
00475 {
00476
00477
00478
00479
00480 Vehicle *v = VehicleFromPos(tile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
00481 if (v == NULL) v = VehicleFromPos(endtile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
00482
00483 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00484 return CommandCost();
00485 }
00486
00487 static Vehicle *EnsureNoTrainOnTrackProc(Vehicle *v, void *data)
00488 {
00489 TrackBits rail_bits = *(TrackBits *)data;
00490
00491 if (v->type != VEH_TRAIN) return NULL;
00492
00493 Train *t = Train::From(v);
00494 if ((t->track != rail_bits) && !TracksOverlap(t->track | rail_bits)) return NULL;
00495
00496 return v;
00497 }
00498
00507 CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
00508 {
00509
00510
00511
00512
00513 Vehicle *v = VehicleFromPos(tile, &track_bits, &EnsureNoTrainOnTrackProc, true);
00514 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00515 return CommandCost();
00516 }
00517
00518 static void UpdateVehicleTileHash(Vehicle *v, bool remove)
00519 {
00520 Vehicle **old_hash = v->hash_tile_current;
00521 Vehicle **new_hash;
00522
00523 if (remove) {
00524 new_hash = NULL;
00525 } else {
00526 int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
00527 int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
00528 new_hash = &_vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
00529 }
00530
00531 if (old_hash == new_hash) return;
00532
00533
00534 if (old_hash != NULL) {
00535 if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = v->hash_tile_prev;
00536 *v->hash_tile_prev = v->hash_tile_next;
00537 }
00538
00539
00540 if (new_hash != NULL) {
00541 v->hash_tile_next = *new_hash;
00542 if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = &v->hash_tile_next;
00543 v->hash_tile_prev = new_hash;
00544 *new_hash = v;
00545 }
00546
00547
00548 v->hash_tile_current = new_hash;
00549 }
00550
00551 static Vehicle *_vehicle_viewport_hash[0x1000];
00552
00553 static void UpdateVehicleViewportHash(Vehicle *v, int x, int y)
00554 {
00555 Vehicle **old_hash, **new_hash;
00556 int old_x = v->coord.left;
00557 int old_y = v->coord.top;
00558
00559 new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(x, y)];
00560 old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(old_x, old_y)];
00561
00562 if (old_hash == new_hash) return;
00563
00564
00565 if (old_hash != NULL) {
00566 if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = v->hash_viewport_prev;
00567 *v->hash_viewport_prev = v->hash_viewport_next;
00568 }
00569
00570
00571 if (new_hash != NULL) {
00572 v->hash_viewport_next = *new_hash;
00573 if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = &v->hash_viewport_next;
00574 v->hash_viewport_prev = new_hash;
00575 *new_hash = v;
00576 }
00577 }
00578
00579 void ResetVehicleHash()
00580 {
00581 Vehicle *v;
00582 FOR_ALL_VEHICLES(v) { v->hash_tile_current = NULL; }
00583 memset(_vehicle_viewport_hash, 0, sizeof(_vehicle_viewport_hash));
00584 memset(_vehicle_tile_hash, 0, sizeof(_vehicle_tile_hash));
00585 }
00586
00587 void ResetVehicleColourMap()
00588 {
00589 Vehicle *v;
00590 FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
00591 }
00592
00597 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
00598 static AutoreplaceMap _vehicles_to_autoreplace;
00599
00600 void InitializeVehicles()
00601 {
00602 _vehicles_to_autoreplace.Reset();
00603 ResetVehicleHash();
00604 }
00605
00606 uint CountVehiclesInChain(const Vehicle *v)
00607 {
00608 uint count = 0;
00609 do count++; while ((v = v->Next()) != NULL);
00610 return count;
00611 }
00612
00617 bool Vehicle::IsEngineCountable() const
00618 {
00619 switch (this->type) {
00620 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
00621 case VEH_TRAIN:
00622 return !this->IsArticulatedPart() &&
00623 !Train::From(this)->IsRearDualheaded();
00624 case VEH_ROAD: return RoadVehicle::From(this)->IsFrontEngine();
00625 case VEH_SHIP: return true;
00626 default: return false;
00627 }
00628 }
00629
00634 bool Vehicle::HasEngineType() const
00635 {
00636 switch (this->type) {
00637 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
00638 case VEH_TRAIN:
00639 case VEH_ROAD:
00640 case VEH_SHIP: return true;
00641 default: return false;
00642 }
00643 }
00644
00650 const Engine *Vehicle::GetEngine() const
00651 {
00652 return Engine::Get(this->engine_type);
00653 }
00654
00660 const GRFFile *Vehicle::GetGRF() const
00661 {
00662 return this->GetEngine()->GetGRF();
00663 }
00664
00670 uint32 Vehicle::GetGRFID() const
00671 {
00672 return this->GetEngine()->GetGRFID();
00673 }
00674
00682 void Vehicle::HandlePathfindingResult(bool path_found)
00683 {
00684 if (path_found) {
00685
00686 if (!HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00687
00688
00689 ClrBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00690
00691 DeleteVehicleNews(this->index, STR_NEWS_VEHICLE_IS_LOST);
00692 return;
00693 }
00694
00695
00696 if (HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00697
00698
00699 SetBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00700
00701 AI::NewEvent(this->owner, new ScriptEventVehicleLost(this->index));
00702 if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) {
00703 SetDParam(0, this->index);
00704 AddVehicleNewsItem(STR_NEWS_VEHICLE_IS_LOST, NS_ADVICE, this->index);
00705 }
00706 }
00707
00709 void Vehicle::PreDestructor()
00710 {
00711 if (CleaningPool()) return;
00712
00713 if (Station::IsValidID(this->last_station_visited)) {
00714 Station *st = Station::Get(this->last_station_visited);
00715 st->loading_vehicles.remove(this);
00716
00717 HideFillingPercent(&this->fill_percent_te_id);
00718 this->CancelReservation(INVALID_STATION, st);
00719 delete this->cargo_payment;
00720 }
00721
00722 if (this->IsEngineCountable()) {
00723 GroupStatistics::CountEngine(this, -1);
00724 if (this->IsPrimaryVehicle()) GroupStatistics::CountVehicle(this, -1);
00725 GroupStatistics::UpdateAutoreplace(this->owner);
00726
00727 if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
00728 DeleteGroupHighlightOfVehicle(this);
00729 }
00730
00731 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
00732 Aircraft *a = Aircraft::From(this);
00733 Station *st = GetTargetAirportIfValid(a);
00734 if (st != NULL) {
00735 const AirportFTA *layout = st->airport.GetFTA()->layout;
00736 CLRBITS(st->airport.flags, layout[a->previous_pos].block | layout[a->pos].block);
00737 }
00738 }
00739
00740
00741 if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
00742 RoadVehicle *v = RoadVehicle::From(this);
00743 if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
00744
00745 RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
00746 }
00747 }
00748
00749 if (this->Previous() == NULL) {
00750 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
00751 }
00752
00753 if (this->IsPrimaryVehicle()) {
00754 DeleteWindowById(WC_VEHICLE_VIEW, this->index);
00755 DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
00756 DeleteWindowById(WC_VEHICLE_REFIT, this->index);
00757 DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
00758 DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
00759 SetWindowDirty(WC_COMPANY, this->owner);
00760 OrderBackup::ClearVehicle(this);
00761 }
00762 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00763
00764 this->cargo.Truncate(0);
00765 DeleteVehicleOrders(this);
00766 DeleteDepotHighlightOfVehicle(this);
00767
00768 extern void StopGlobalFollowVehicle(const Vehicle *v);
00769 StopGlobalFollowVehicle(this);
00770
00771 ReleaseDisastersTargetingVehicle(this->index);
00772 }
00773
00774 Vehicle::~Vehicle()
00775 {
00776 free(this->name);
00777
00778 if (CleaningPool()) {
00779 this->cargo.OnCleanPool();
00780 return;
00781 }
00782
00783
00784
00785 if (!(this->vehstatus & VS_HIDDEN)) MarkSingleVehicleDirty(this);
00786
00787 Vehicle *v = this->Next();
00788 this->SetNext(NULL);
00789
00790 delete v;
00791
00792 UpdateVehicleTileHash(this, true);
00793 UpdateVehicleViewportHash(this, INVALID_COORD, 0);
00794 DeleteVehicleNews(this->index, INVALID_STRING_ID);
00795 DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index);
00796 }
00797
00802 void VehicleEnteredDepotThisTick(Vehicle *v)
00803 {
00804
00805 _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
00806
00807
00808
00809
00810
00811
00812 v->vehstatus |= VS_STOPPED;
00813 }
00814
00820 static void RunVehicleDayProc()
00821 {
00822 if (_game_mode != GM_NORMAL) return;
00823
00824
00825 for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
00826 Vehicle *v = Vehicle::Get(i);
00827 if (v == NULL) continue;
00828
00829
00830 if ((v->day_counter & 0x1F) == 0 && v->HasEngineType()) {
00831 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
00832 if (callback != CALLBACK_FAILED) {
00833 if (HasBit(callback, 0)) TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32);
00834 if (HasBit(callback, 1)) v->colourmap = PAL_NONE;
00835
00836 if (callback & ~3) ErrorUnknownCallbackResult(v->GetGRFID(), CBID_VEHICLE_32DAY_CALLBACK, callback);
00837 }
00838 }
00839
00840
00841 v->OnNewDay();
00842 }
00843 }
00844
00845 void CallVehicleTicks()
00846 {
00847 _vehicles_to_autoreplace.Clear();
00848
00849 RunVehicleDayProc();
00850
00851 Station *st;
00852 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
00853
00854 Vehicle *v;
00855 FOR_ALL_VEHICLES(v) {
00856
00857 if (!v->Tick()) {
00858 assert(Vehicle::Get(vehicle_index) == NULL);
00859 continue;
00860 }
00861
00862 assert(Vehicle::Get(vehicle_index) == v);
00863
00864 switch (v->type) {
00865 default: break;
00866
00867 case VEH_TRAIN:
00868 case VEH_ROAD:
00869 case VEH_AIRCRAFT:
00870 case VEH_SHIP:
00871 if (v->vcache.cached_cargo_age_period != 0) {
00872 v->cargo_age_counter = min(v->cargo_age_counter, v->vcache.cached_cargo_age_period);
00873 if (--v->cargo_age_counter == 0) {
00874 v->cargo.AgeCargo();
00875 v->cargo_age_counter = v->vcache.cached_cargo_age_period;
00876 }
00877 }
00878
00879 if (v->type == VEH_TRAIN && Train::From(v)->IsWagon()) continue;
00880 if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
00881 if (v->type == VEH_ROAD && !RoadVehicle::From(v)->IsFrontEngine()) continue;
00882
00883 v->motion_counter += v->cur_speed;
00884
00885 if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
00886
00887
00888 if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
00889 }
00890 }
00891
00892 Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
00893 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
00894 v = it->first;
00895
00896 cur_company.Change(v->owner);
00897
00898
00899
00900
00901 if (it->second) v->vehstatus &= ~VS_STOPPED;
00902
00903
00904 int x = v->x_pos;
00905 int y = v->y_pos;
00906 int z = v->z_pos;
00907
00908 const Company *c = Company::Get(_current_company);
00909 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
00910 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
00911 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
00912
00913 if (!IsLocalCompany()) continue;
00914
00915 if (res.Succeeded()) {
00916 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
00917 continue;
00918 }
00919
00920 StringID error_message = res.GetErrorMessage();
00921 if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
00922
00923 if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
00924
00925 StringID message;
00926 if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
00927 message = error_message;
00928 } else {
00929 message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
00930 }
00931
00932 SetDParam(0, v->index);
00933 SetDParam(1, error_message);
00934 AddVehicleNewsItem(message, NS_ADVICE, v->index);
00935 }
00936
00937 cur_company.Restore();
00938 }
00939
00944 static void DoDrawVehicle(const Vehicle *v)
00945 {
00946 SpriteID image = v->cur_image;
00947 PaletteID pal = PAL_NONE;
00948
00949 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
00950
00951
00952 bool shadowed = (v->vehstatus & VS_SHADOW) != 0;
00953
00954 if (v->type == VEH_EFFECT) {
00955
00956
00957 TransparencyOption to = EffectVehicle::From(v)->GetTransparencyOption();
00958 if (to != TO_INVALID && (IsTransparencySet(to) || IsInvisibilitySet(to))) return;
00959 }
00960
00961 AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
00962 v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs);
00963 }
00964
00969 void ViewportAddVehicles(DrawPixelInfo *dpi)
00970 {
00971
00972 const int l = dpi->left;
00973 const int r = dpi->left + dpi->width;
00974 const int t = dpi->top;
00975 const int b = dpi->top + dpi->height;
00976
00977
00978 int xl, xu, yl, yu;
00979
00980 if (dpi->width + (70 * ZOOM_LVL_BASE) < (1 << (7 + 6 + ZOOM_LVL_SHIFT))) {
00981 xl = GB(l - (70 * ZOOM_LVL_BASE), 7 + ZOOM_LVL_SHIFT, 6);
00982 xu = GB(r, 7 + ZOOM_LVL_SHIFT, 6);
00983 } else {
00984
00985 xl = 0;
00986 xu = 0x3F;
00987 }
00988
00989 if (dpi->height + (70 * ZOOM_LVL_BASE) < (1 << (6 + 6 + ZOOM_LVL_SHIFT))) {
00990 yl = GB(t - (70 * ZOOM_LVL_BASE), 6 + ZOOM_LVL_SHIFT, 6) << 6;
00991 yu = GB(b, 6 + ZOOM_LVL_SHIFT, 6) << 6;
00992 } else {
00993
00994 yl = 0;
00995 yu = 0x3F << 6;
00996 }
00997
00998 for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
00999 for (int x = xl;; x = (x + 1) & 0x3F) {
01000 const Vehicle *v = _vehicle_viewport_hash[x + y];
01001
01002 while (v != NULL) {
01003 if (!(v->vehstatus & VS_HIDDEN) &&
01004 l <= v->coord.right &&
01005 t <= v->coord.bottom &&
01006 r >= v->coord.left &&
01007 b >= v->coord.top) {
01008 DoDrawVehicle(v);
01009 }
01010 v = v->hash_viewport_next;
01011 }
01012
01013 if (x == xu) break;
01014 }
01015
01016 if (y == yu) break;
01017 }
01018 }
01019
01027 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
01028 {
01029 Vehicle *found = NULL, *v;
01030 uint dist, best_dist = UINT_MAX;
01031
01032 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
01033
01034 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
01035 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
01036
01037 FOR_ALL_VEHICLES(v) {
01038 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
01039 x >= v->coord.left && x <= v->coord.right &&
01040 y >= v->coord.top && y <= v->coord.bottom) {
01041
01042 dist = max(
01043 abs(((v->coord.left + v->coord.right) >> 1) - x),
01044 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
01045 );
01046
01047 if (dist < best_dist) {
01048 found = v;
01049 best_dist = dist;
01050 }
01051 }
01052 }
01053
01054 return found;
01055 }
01056
01061 void DecreaseVehicleValue(Vehicle *v)
01062 {
01063 v->value -= v->value >> 8;
01064 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01065 }
01066
01067 static const byte _breakdown_chance[64] = {
01068 3, 3, 3, 3, 3, 3, 3, 3,
01069 4, 4, 5, 5, 6, 6, 7, 7,
01070 8, 8, 9, 9, 10, 10, 11, 11,
01071 12, 13, 13, 13, 13, 14, 15, 16,
01072 17, 19, 21, 25, 28, 31, 34, 37,
01073 40, 44, 48, 52, 56, 60, 64, 68,
01074 72, 80, 90, 100, 110, 120, 130, 140,
01075 150, 170, 190, 210, 230, 250, 250, 250,
01076 };
01077
01078 void CheckVehicleBreakdown(Vehicle *v)
01079 {
01080 int rel, rel_old;
01081
01082
01083 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
01084 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01085
01086 if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
01087 _settings_game.difficulty.vehicle_breakdowns < 1 ||
01088 v->cur_speed < 5 || _game_mode == GM_MENU) {
01089 return;
01090 }
01091
01092 uint32 r = Random();
01093
01094
01095 int chance = v->breakdown_chance + 1;
01096 if (Chance16I(1, 25, r)) chance += 25;
01097 v->breakdown_chance = min(255, chance);
01098
01099
01100 rel = v->reliability;
01101 if (v->type == VEH_SHIP) rel += 0x6666;
01102
01103
01104 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
01105
01106
01107 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
01108 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
01109 v->breakdown_delay = GB(r, 24, 7) + 0x80;
01110 v->breakdown_chance = 0;
01111 }
01112 }
01113
01120 bool Vehicle::HandleBreakdown()
01121 {
01122
01123
01124
01125
01126
01127 switch (this->breakdown_ctr) {
01128 case 0:
01129 return false;
01130
01131 case 2:
01132 this->breakdown_ctr = 1;
01133
01134 if (this->breakdowns_since_last_service != 255) {
01135 this->breakdowns_since_last_service++;
01136 }
01137
01138 if (this->type == VEH_AIRCRAFT) {
01139
01140 this->vehstatus |= VS_AIRCRAFT_BROKEN;
01141 } else {
01142 this->cur_speed = 0;
01143
01144 if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
01145 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
01146 (this->type == VEH_TRAIN ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
01147 (this->type == VEH_TRAIN ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
01148 }
01149
01150 if (!(this->vehstatus & VS_HIDDEN)) {
01151 EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
01152 if (u != NULL) u->animation_state = this->breakdown_delay * 2;
01153 }
01154 }
01155
01156 this->MarkDirty();
01157 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01158 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01159
01160
01161 case 1:
01162
01163 if (this->type == VEH_AIRCRAFT) return false;
01164
01165
01166 if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
01167 if (--this->breakdown_delay == 0) {
01168 this->breakdown_ctr = 0;
01169 this->MarkDirty();
01170 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01171 }
01172 }
01173 return true;
01174
01175 default:
01176 if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
01177 return false;
01178 }
01179 }
01180
01185 void AgeVehicle(Vehicle *v)
01186 {
01187 if (v->age < MAX_DAY) {
01188 v->age++;
01189 if (v->IsPrimaryVehicle() && v->age == VEHICLE_PROFIT_MIN_AGE + 1) GroupStatistics::VehicleReachedProfitAge(v);
01190 }
01191
01192 if (!v->IsPrimaryVehicle() && (v->type != VEH_TRAIN || !Train::From(v)->IsEngine())) return;
01193
01194 int age = v->age - v->max_age;
01195 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
01196 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
01197 v->reliability_spd_dec <<= 1;
01198 }
01199
01200 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01201
01202
01203 if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
01204
01205
01206 if (Company::Get(v->owner)->settings.engine_renew && v->GetEngine()->company_avail != 0) return;
01207
01208 StringID str;
01209 if (age == -DAYS_IN_LEAP_YEAR) {
01210 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
01211 } else if (age == 0) {
01212 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
01213 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
01214 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
01215 } else {
01216 return;
01217 }
01218
01219 SetDParam(0, v->index);
01220 AddVehicleNewsItem(str, NS_ADVICE, v->index);
01221 }
01222
01229 uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *colour)
01230 {
01231 int count = 0;
01232 int max = 0;
01233 int cars = 0;
01234 int unloading = 0;
01235 bool loading = false;
01236
01237 const Vehicle *u = v;
01238
01239 const Station *st = Station::GetIfValid(v->last_station_visited);
01240 assert(colour == NULL || st != NULL);
01241
01242
01243 for (; v != NULL; v = v->Next()) {
01244 count += v->cargo.OnboardCount();
01245 max += v->cargo_cap;
01246 if (v->cargo_cap != 0 && colour != NULL) {
01247 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
01248 loading |= !(u->current_order.GetLoadType() & OLFB_NO_LOAD) && st->goods[v->cargo_type].days_since_pickup != 255;
01249 cars++;
01250 }
01251 }
01252
01253 if (colour != NULL) {
01254 if (unloading == 0 && loading) {
01255 *colour = STR_PERCENT_UP;
01256 } else if (cars == unloading || !loading) {
01257 *colour = STR_PERCENT_DOWN;
01258 } else {
01259 *colour = STR_PERCENT_UP_DOWN;
01260 }
01261 }
01262
01263
01264 if (max == 0) return 100;
01265
01266
01267 return (count * 100) / max;
01268 }
01269
01274 void VehicleEnterDepot(Vehicle *v)
01275 {
01276
01277 assert(v == v->First());
01278
01279 switch (v->type) {
01280 case VEH_TRAIN: {
01281 Train *t = Train::From(v);
01282 SetWindowClassesDirty(WC_TRAINS_LIST);
01283
01284 SetDepotReservation(t->tile, false);
01285 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
01286
01287 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
01288 t->wait_counter = 0;
01289 t->force_proceed = TFP_NONE;
01290 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
01291 t->ConsistChanged(true);
01292 break;
01293 }
01294
01295 case VEH_ROAD:
01296 SetWindowClassesDirty(WC_ROADVEH_LIST);
01297 break;
01298
01299 case VEH_SHIP: {
01300 SetWindowClassesDirty(WC_SHIPS_LIST);
01301 Ship *ship = Ship::From(v);
01302 ship->state = TRACK_BIT_DEPOT;
01303 ship->UpdateCache();
01304 ship->UpdateViewport(true, true);
01305 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01306 break;
01307 }
01308
01309 case VEH_AIRCRAFT:
01310 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01311 HandleAircraftEnterHangar(Aircraft::From(v));
01312 break;
01313 default: NOT_REACHED();
01314 }
01315 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01316
01317 if (v->type != VEH_TRAIN) {
01318
01319
01320 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01321 }
01322 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01323
01324 v->vehstatus |= VS_HIDDEN;
01325 v->cur_speed = 0;
01326
01327 VehicleServiceInDepot(v);
01328
01329 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01330
01331 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01332 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01333
01334 const Order *real_order = v->GetOrder(v->cur_real_order_index);
01335 Order t = v->current_order;
01336 v->current_order.MakeDummy();
01337
01338
01339
01340 if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
01341 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
01342 (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
01343
01344 return;
01345 }
01346
01347 if (t.IsRefit()) {
01348 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01349 CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
01350 cur_company.Restore();
01351
01352 if (cost.Failed()) {
01353 _vehicles_to_autoreplace[v] = false;
01354 if (v->owner == _local_company) {
01355
01356 SetDParam(0, v->index);
01357 AddVehicleNewsItem(STR_NEWS_ORDER_REFIT_FAILED, NS_ADVICE, v->index);
01358 }
01359 } else if (cost.GetCost() != 0) {
01360 v->profit_this_year -= cost.GetCost() << 8;
01361 if (v->owner == _local_company) {
01362 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01363 }
01364 }
01365 }
01366
01367 if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01368
01369 v->DeleteUnreachedImplicitOrders();
01370 UpdateVehicleTimetable(v, true);
01371 v->IncrementImplicitOrderIndex();
01372 }
01373 if (t.GetDepotActionType() & ODATFB_HALT) {
01374
01375 _vehicles_to_autoreplace[v] = false;
01376 if (v->owner == _local_company) {
01377 SetDParam(0, v->index);
01378 AddVehicleNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, NS_ADVICE, v->index);
01379 }
01380 AI::NewEvent(v->owner, new ScriptEventVehicleWaitingInDepot(v->index));
01381 }
01382 }
01383 }
01384
01385
01391 void VehicleUpdatePosition(Vehicle *v)
01392 {
01393 UpdateVehicleTileHash(v, false);
01394 }
01395
01402 void VehicleUpdateViewport(Vehicle *v, bool dirty)
01403 {
01404 int img = v->cur_image;
01405 Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
01406 const Sprite *spr = GetSprite(img, ST_NORMAL);
01407
01408 pt.x += spr->x_offs;
01409 pt.y += spr->y_offs;
01410
01411 UpdateVehicleViewportHash(v, pt.x, pt.y);
01412
01413 Rect old_coord = v->coord;
01414 v->coord.left = pt.x;
01415 v->coord.top = pt.y;
01416 v->coord.right = pt.x + spr->width + 2 * ZOOM_LVL_BASE;
01417 v->coord.bottom = pt.y + spr->height + 2 * ZOOM_LVL_BASE;
01418
01419 if (dirty) {
01420 MarkAllViewportsDirty(
01421 min(old_coord.left, v->coord.left),
01422 min(old_coord.top, v->coord.top),
01423 max(old_coord.right, v->coord.right) + 1 * ZOOM_LVL_BASE,
01424 max(old_coord.bottom, v->coord.bottom) + 1 * ZOOM_LVL_BASE
01425 );
01426 }
01427 }
01428
01433 void VehicleUpdatePositionAndViewport(Vehicle *v)
01434 {
01435 VehicleUpdatePosition(v);
01436 VehicleUpdateViewport(v, true);
01437 }
01438
01443 void MarkSingleVehicleDirty(const Vehicle *v)
01444 {
01445 MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1 * ZOOM_LVL_BASE, v->coord.bottom + 1 * ZOOM_LVL_BASE);
01446 }
01447
01453 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01454 {
01455 static const int8 _delta_coord[16] = {
01456 -1,-1,-1, 0, 1, 1, 1, 0,
01457 -1, 0, 1, 1, 1, 0,-1,-1,
01458 };
01459
01460 int x = v->x_pos + _delta_coord[v->direction];
01461 int y = v->y_pos + _delta_coord[v->direction + 8];
01462
01463 GetNewVehiclePosResult gp;
01464 gp.x = x;
01465 gp.y = y;
01466 gp.old_tile = v->tile;
01467 gp.new_tile = TileVirtXY(x, y);
01468 return gp;
01469 }
01470
01471 static const Direction _new_direction_table[] = {
01472 DIR_N, DIR_NW, DIR_W,
01473 DIR_NE, DIR_SE, DIR_SW,
01474 DIR_E, DIR_SE, DIR_S
01475 };
01476
01477 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01478 {
01479 int i = 0;
01480
01481 if (y >= v->y_pos) {
01482 if (y != v->y_pos) i += 3;
01483 i += 3;
01484 }
01485
01486 if (x >= v->x_pos) {
01487 if (x != v->x_pos) i++;
01488 i++;
01489 }
01490
01491 Direction dir = v->direction;
01492
01493 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01494 if (dirdiff == DIRDIFF_SAME) return dir;
01495 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01496 }
01497
01507 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01508 {
01509 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01510 }
01511
01519 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01520 {
01521
01522 const Vehicle *v;
01523 FOR_ALL_VEHICLES(v) {
01524 if (v->type == type && v->owner == owner) {
01525 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01526 }
01527 }
01528
01529 if (this->maxid == 0) return;
01530
01531
01532
01533
01534 this->cache = CallocT<bool>(this->maxid + 2);
01535
01536
01537 FOR_ALL_VEHICLES(v) {
01538 if (v->type == type && v->owner == owner) {
01539 this->cache[v->unitnumber] = true;
01540 }
01541 }
01542 }
01543
01545 UnitID FreeUnitIDGenerator::NextID()
01546 {
01547 if (this->maxid <= this->curid) return ++this->curid;
01548
01549 while (this->cache[++this->curid]) { }
01550
01551 return this->curid;
01552 }
01553
01559 UnitID GetFreeUnitNumber(VehicleType type)
01560 {
01561
01562 uint max_veh;
01563 switch (type) {
01564 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
01565 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
01566 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
01567 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
01568 default: NOT_REACHED();
01569 }
01570
01571 const Company *c = Company::Get(_current_company);
01572 if (c->group_all[type].num_vehicle >= max_veh) return UINT16_MAX;
01573
01574 FreeUnitIDGenerator gen(type, _current_company);
01575
01576 return gen.NextID();
01577 }
01578
01579
01588 bool CanBuildVehicleInfrastructure(VehicleType type)
01589 {
01590 assert(IsCompanyBuildableVehicleType(type));
01591
01592 if (!Company::IsValidID(_local_company)) return false;
01593 if (!_settings_client.gui.disable_unsuitable_building) return true;
01594
01595 UnitID max;
01596 switch (type) {
01597 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
01598 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
01599 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
01600 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01601 default: NOT_REACHED();
01602 }
01603
01604
01605 if (max > 0) {
01606
01607 const Engine *e;
01608 FOR_ALL_ENGINES_OF_TYPE(e, type) {
01609 if (HasBit(e->company_avail, _local_company)) return true;
01610 }
01611 return false;
01612 }
01613
01614
01615 const Vehicle *v;
01616 FOR_ALL_VEHICLES(v) {
01617 if (v->owner == _local_company && v->type == type) return true;
01618 }
01619
01620 return false;
01621 }
01622
01623
01631 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
01632 {
01633 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01634 const Engine *e = Engine::Get(engine_type);
01635 switch (e->type) {
01636 default: NOT_REACHED();
01637 case VEH_TRAIN:
01638 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
01639
01640
01641 engine_type = parent_engine_type;
01642 e = Engine::Get(engine_type);
01643
01644 }
01645
01646 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01647 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01648 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
01649 if (!CargoSpec::Get(cargo_type)->is_freight) {
01650 if (parent_engine_type == INVALID_ENGINE) {
01651 return LS_PASSENGER_WAGON_STEAM;
01652 } else {
01653 switch (RailVehInfo(parent_engine_type)->engclass) {
01654 default: NOT_REACHED();
01655 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
01656 case EC_DIESEL: return LS_PASSENGER_WAGON_DIESEL;
01657 case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
01658 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
01659 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
01660 }
01661 }
01662 } else {
01663 return LS_FREIGHT_WAGON;
01664 }
01665 } else {
01666 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
01667
01668 switch (e->u.rail.engclass) {
01669 default: NOT_REACHED();
01670 case EC_STEAM: return LS_STEAM;
01671 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
01672 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
01673 case EC_MONORAIL: return LS_MONORAIL;
01674 case EC_MAGLEV: return LS_MAGLEV;
01675 }
01676 }
01677
01678 case VEH_ROAD:
01679
01680 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01681 engine_type = parent_engine_type;
01682 e = Engine::Get(engine_type);
01683 cargo_type = v->First()->cargo_type;
01684 }
01685 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01686 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01687
01688
01689 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
01690
01691 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01692 } else {
01693
01694 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01695 }
01696
01697 case VEH_SHIP:
01698 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01699 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01700 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01701
01702 case VEH_AIRCRAFT:
01703 switch (e->u.air.subtype) {
01704 case AIR_HELI: return LS_HELICOPTER;
01705 case AIR_CTOL: return LS_SMALL_PLANE;
01706 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
01707 default: NOT_REACHED();
01708 }
01709 }
01710 }
01711
01721 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
01722 {
01723 const Company *c = Company::Get(company);
01724 LiveryScheme scheme = LS_DEFAULT;
01725
01726
01727
01728 if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
01729
01730 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
01731
01732
01733 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01734 }
01735
01736 return &c->livery[scheme];
01737 }
01738
01739
01740 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01741 {
01742 PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01743
01744
01745 if (map != PAL_NONE) return map;
01746
01747 const Engine *e = Engine::Get(engine_type);
01748
01749
01750 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
01751 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01752
01753 if (callback != CALLBACK_FAILED) {
01754 assert_compile(PAL_NONE == 0);
01755 map = GB(callback, 0, 14);
01756
01757
01758 if (!HasBit(callback, 14)) {
01759
01760 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01761 return map;
01762 }
01763 }
01764 }
01765
01766 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
01767
01768 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
01769
01770
01771 if (!Company::IsValidID(company)) return map;
01772
01773 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
01774
01775 map += livery->colour1;
01776 if (twocc) map += livery->colour2 * 16;
01777
01778
01779 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01780 return map;
01781 }
01782
01789 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
01790 {
01791 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01792 }
01793
01799 PaletteID GetVehiclePalette(const Vehicle *v)
01800 {
01801 if (v->IsGroundVehicle()) {
01802 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
01803 }
01804
01805 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01806 }
01807
01811 void Vehicle::DeleteUnreachedImplicitOrders()
01812 {
01813 if (this->IsGroundVehicle()) {
01814 uint16 &gv_flags = this->GetGroundVehicleFlags();
01815 if (HasBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)) {
01816
01817 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
01818 this->cur_implicit_order_index = this->cur_real_order_index;
01819 InvalidateVehicleOrder(this, 0);
01820 return;
01821 }
01822 }
01823
01824 const Order *order = this->GetOrder(this->cur_implicit_order_index);
01825 while (order != NULL) {
01826 if (this->cur_implicit_order_index == this->cur_real_order_index) break;
01827
01828 if (order->IsType(OT_IMPLICIT)) {
01829
01830 order = order->next;
01831 DeleteOrder(this, this->cur_implicit_order_index);
01832 } else {
01833
01834 order = order->next;
01835 this->cur_implicit_order_index++;
01836 }
01837
01838
01839 if (order == NULL) {
01840 order = this->GetOrder(0);
01841 this->cur_implicit_order_index = 0;
01842 }
01843 }
01844 }
01845
01850 void Vehicle::BeginLoading()
01851 {
01852 assert(IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP);
01853
01854 if (this->current_order.IsType(OT_GOTO_STATION) &&
01855 this->current_order.GetDestination() == this->last_station_visited) {
01856 this->DeleteUnreachedImplicitOrders();
01857
01858
01859 this->current_order.MakeLoading(true);
01860 UpdateVehicleTimetable(this, true);
01861
01862
01863
01864
01865
01866
01867 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
01868
01869 } else {
01870
01871
01872
01873
01874
01875 Order *in_list = this->GetOrder(this->cur_implicit_order_index);
01876 if (this->IsGroundVehicle() && in_list != NULL &&
01877 (!in_list->IsType(OT_IMPLICIT) ||
01878 in_list->GetDestination() != this->last_station_visited)) {
01879 bool suppress_implicit_orders = HasBit(this->GetGroundVehicleFlags(), GVF_SUPPRESS_IMPLICIT_ORDERS);
01880
01881 Order *prev_order = this->cur_implicit_order_index > 0 ? this->GetOrder(this->cur_implicit_order_index - 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : NULL);
01882 if (prev_order == NULL ||
01883 (!prev_order->IsType(OT_IMPLICIT) && !prev_order->IsType(OT_GOTO_STATION)) ||
01884 prev_order->GetDestination() != this->last_station_visited) {
01885
01886
01887
01888 int target_index = this->cur_implicit_order_index;
01889 bool found = false;
01890 while (target_index != this->cur_real_order_index) {
01891 const Order *order = this->GetOrder(target_index);
01892 if (order->IsType(OT_IMPLICIT) && order->GetDestination() == this->last_station_visited) {
01893 found = true;
01894 break;
01895 }
01896 target_index++;
01897 if (target_index >= this->orders.list->GetNumOrders()) target_index = 0;
01898 assert(target_index != this->cur_implicit_order_index);
01899 }
01900
01901 if (found) {
01902 if (suppress_implicit_orders) {
01903
01904 this->cur_implicit_order_index = target_index;
01905 InvalidateVehicleOrder(this, 0);
01906 } else {
01907
01908 const Order *order = this->GetOrder(this->cur_implicit_order_index);
01909 while (!order->IsType(OT_IMPLICIT) || order->GetDestination() != this->last_station_visited) {
01910 if (order->IsType(OT_IMPLICIT)) {
01911
01912 order = order->next;
01913 DeleteOrder(this, this->cur_implicit_order_index);
01914 } else {
01915
01916 order = order->next;
01917 this->cur_implicit_order_index++;
01918 }
01919
01920
01921 if (order == NULL) {
01922 order = this->GetOrder(0);
01923 this->cur_implicit_order_index = 0;
01924 }
01925 assert(order != NULL);
01926 }
01927 }
01928 } else if (!suppress_implicit_orders && this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID && Order::CanAllocateItem()) {
01929
01930 Order *implicit_order = new Order();
01931 implicit_order->MakeImplicit(this->last_station_visited);
01932 InsertOrder(this, implicit_order, this->cur_implicit_order_index);
01933 if (this->cur_implicit_order_index > 0) --this->cur_implicit_order_index;
01934
01935
01936
01937 uint16 &gv_flags = this->GetGroundVehicleFlags();
01938 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
01939 }
01940 }
01941 }
01942 this->current_order.MakeLoading(false);
01943 }
01944
01945 if (this->last_loading_station != INVALID_STATION &&
01946 this->last_loading_station != this->last_station_visited &&
01947 ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
01948 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0)) {
01949 IncreaseStats(Station::Get(this->last_loading_station), this, this->last_station_visited);
01950 }
01951
01952 PrepareUnload(this);
01953
01954 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
01955 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
01956 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01957 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
01958
01959 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
01960 this->cur_speed = 0;
01961 this->MarkDirty();
01962 }
01963
01968 void Vehicle::CancelReservation(StationID next, Station *st)
01969 {
01970 for (Vehicle *v = this; v != NULL; v = v->next) {
01971 VehicleCargoList &cargo = v->cargo;
01972 if (cargo.ReservedCount() > 0) {
01973 DEBUG(misc, 1, "cancelling cargo reservation");
01974 GoodsEntry &ge = st->goods[v->cargo_type];
01975 cargo.Unreserve(next, &ge.cargo);
01976 SetBit(ge.acceptance_pickup, GoodsEntry::GES_PICKUP);
01977 }
01978 }
01979 }
01980
01985 void Vehicle::LeaveStation()
01986 {
01987 assert(this->current_order.IsType(OT_LOADING));
01988
01989 delete this->cargo_payment;
01990
01991
01992 if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
01993
01994 if ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
01995 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
01996 if (this->current_order.CanLeaveWithCargo(this->last_loading_station != INVALID_STATION)) {
01997
01998
01999
02000
02001 this->RefreshNextHopsStats();
02002
02003
02004 this->last_loading_station = this->last_station_visited;
02005 } else {
02006
02007
02008
02009 this->last_loading_station = INVALID_STATION;
02010 }
02011 }
02012
02013 this->current_order.MakeLeaveStation();
02014 Station *st = Station::Get(this->last_station_visited);
02015 this->CancelReservation(INVALID_STATION, st);
02016 st->loading_vehicles.remove(this);
02017
02018 HideFillingPercent(&this->fill_percent_te_id);
02019
02020 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
02021
02022 if (IsTileType(this->tile, MP_STATION)) TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
02023
02024 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
02025 }
02026 }
02027
02034 void Vehicle::RefreshNextHopsStats()
02035 {
02036
02037 SmallMap<CargoID, uint, 1> capacities;
02038 for (Vehicle *v = this; v != NULL; v = v->Next()) {
02039 v->refit_cap = v->cargo_cap;
02040 if (v->refit_cap == 0) continue;
02041 SmallPair<CargoID, uint> *i = capacities.Find(v->cargo_type);
02042 if (i == capacities.End()) {
02043
02044 i = capacities.Append();
02045 i->first = v->cargo_type;
02046 i->second = v->cargo_cap;
02047 } else {
02048 i->second += v->cargo_cap;
02049 }
02050 }
02051
02052
02053 if (this->orders.list == NULL) return;
02054
02055 uint hops = 0;
02056 const Order *first = this->orders.list->GetNextStoppingOrder(this,
02057 this->GetOrder(this->cur_implicit_order_index), hops);
02058 const Order *cur = first;
02059 const Order *next = first;
02060 while (next != NULL && cur->CanLeaveWithCargo(true)) {
02061 next = this->orders.list->GetNextStoppingOrder(this,
02062 this->orders.list->GetNext(next), ++hops);
02063 if (next == NULL) break;
02064
02065 if (next->IsType(OT_GOTO_DEPOT)) {
02066
02067 CargoID new_cid = next->GetRefitCargo();
02068 byte new_subtype = next->GetRefitSubtype();
02069 for (Vehicle *v = this; v != NULL; v = v->Next()) {
02070 const Engine *e = Engine::Get(v->engine_type);
02071 if (!HasBit(e->info.refit_mask, new_cid)) continue;
02072
02073
02074 CargoID temp_cid = v->cargo_type;
02075 byte temp_subtype = v->cargo_subtype;
02076 v->cargo_type = new_cid;
02077 v->cargo_subtype = new_subtype;
02078
02079 uint16 mail_capacity = 0;
02080 uint amount = e->DetermineCapacity(v, &mail_capacity);
02081
02082
02083 v->cargo_type = temp_cid;
02084 v->cargo_subtype = temp_subtype;
02085
02086
02087 if (new_cid != v->cargo_type && v->refit_cap > 0) {
02088 capacities[v->cargo_type] -= v->refit_cap;
02089 v->refit_cap = 0;
02090 } else if (amount < v->refit_cap) {
02091 capacities[v->cargo_type] -= v->refit_cap - amount;
02092 v->refit_cap = amount;
02093 }
02094
02095
02096 if (v->type == VEH_AIRCRAFT) {
02097 Vehicle *u = v->Next();
02098 if (mail_capacity < u->refit_cap) {
02099 capacities[u->cargo_type] -= u->refit_cap - mail_capacity;
02100 u->refit_cap = mail_capacity;
02101 }
02102 break;
02103 }
02104 if (v->type == VEH_SHIP) break;
02105 }
02106 } else {
02107 StationID next_station = next->GetDestination();
02108 Station *st = Station::GetIfValid(cur->GetDestination());
02109 if (st != NULL && next_station != INVALID_STATION && next_station != st->index) {
02110 for (const SmallPair<CargoID, uint> *i = capacities.Begin(); i != capacities.End(); ++i) {
02111
02112 if (i->second > 0) IncreaseStats(st, i->first, next_station, i->second, UINT_MAX);
02113 }
02114 }
02115 cur = next;
02116 if (cur == first) break;
02117 }
02118 }
02119
02120 for (Vehicle *v = this; v != NULL; v = v->Next()) v->refit_cap = v->cargo_cap;
02121 }
02122
02128 void Vehicle::HandleLoading(bool mode)
02129 {
02130 switch (this->current_order.GetType()) {
02131 case OT_LOADING: {
02132 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
02133
02134
02135 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
02136
02137 this->PlayLeaveStationSound();
02138
02139 this->LeaveStation();
02140
02141
02142 const Order *order = this->GetOrder(this->cur_implicit_order_index);
02143 if (order == NULL ||
02144 (!order->IsType(OT_IMPLICIT) && !order->IsType(OT_GOTO_STATION)) ||
02145 order->GetDestination() != this->last_station_visited) {
02146 return;
02147 }
02148 break;
02149 }
02150
02151 case OT_DUMMY: break;
02152
02153 default: return;
02154 }
02155
02156 this->IncrementImplicitOrderIndex();
02157 }
02158
02163 void Vehicle::GetConsistFreeCapacities(SmallMap<CargoID, uint> &capacities) const
02164 {
02165 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
02166 if (v->cargo_cap == 0) continue;
02167 SmallPair<CargoID, uint> *pair = capacities.Find(v->cargo_type);
02168 if (pair == capacities.End()) {
02169 pair = capacities.Append();
02170 pair->first = v->cargo_type;
02171 pair->second = v->cargo_cap - v->cargo.Count();
02172 } else {
02173 pair->second += v->cargo_cap - v->cargo.Count();
02174 }
02175 }
02176 }
02177
02178 uint Vehicle::GetConsistTotalCapacity() const
02179 {
02180 uint result = 0;
02181 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
02182 result += v->cargo_cap;
02183 }
02184 return result;
02185 }
02186
02193 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
02194 {
02195 CommandCost ret = CheckOwnership(this->owner);
02196 if (ret.Failed()) return ret;
02197
02198 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
02199 if (this->IsStoppedInDepot()) return CMD_ERROR;
02200
02201 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
02202 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
02203 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
02204
02205
02206
02207 if (flags & DC_EXEC) {
02208 this->current_order.SetDepotOrderType(ODTF_MANUAL);
02209 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
02210 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02211 }
02212 return CommandCost();
02213 }
02214
02215 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR;
02216 if (flags & DC_EXEC) {
02217
02218
02219 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
02220
02221 if (this->IsGroundVehicle()) {
02222 uint16 &gv_flags = this->GetGroundVehicleFlags();
02223 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02224 }
02225
02226 this->current_order.MakeDummy();
02227 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02228 }
02229 return CommandCost();
02230 }
02231
02232 TileIndex location;
02233 DestinationID destination;
02234 bool reverse;
02235 static const StringID no_depot[] = {STR_ERROR_UNABLE_TO_FIND_ROUTE_TO, STR_ERROR_UNABLE_TO_FIND_LOCAL_DEPOT, STR_ERROR_UNABLE_TO_FIND_LOCAL_DEPOT, STR_ERROR_CAN_T_SEND_AIRCRAFT_TO_HANGAR};
02236 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
02237
02238 if (flags & DC_EXEC) {
02239 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
02240
02241 if (this->IsGroundVehicle()) {
02242 uint16 &gv_flags = this->GetGroundVehicleFlags();
02243 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02244 }
02245
02246 this->dest_tile = location;
02247 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
02248 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
02249 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02250
02251
02252 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
02253
02254 if (this->type == VEH_AIRCRAFT) {
02255 Aircraft *a = Aircraft::From(this);
02256 if (a->state == FLYING && a->targetairport != destination) {
02257
02258 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
02259 AircraftNextAirportPos_and_Order(a);
02260 }
02261 }
02262 }
02263
02264 return CommandCost();
02265
02266 }
02267
02272 void Vehicle::UpdateVisualEffect(bool allow_power_change)
02273 {
02274 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02275 const Engine *e = this->GetEngine();
02276
02277
02278 byte visual_effect;
02279 switch (e->type) {
02280 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
02281 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
02282 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
02283 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
02284 }
02285
02286
02287 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
02288 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
02289
02290 if (callback != CALLBACK_FAILED) {
02291 if (callback >= 0x100 && e->GetGRF()->grf_version >= 8) ErrorUnknownCallbackResult(e->GetGRFID(), CBID_VEHICLE_VISUAL_EFFECT, callback);
02292
02293 callback = GB(callback, 0, 8);
02294
02295
02296 if (callback == VE_DEFAULT) {
02297 assert(HasBit(callback, VE_DISABLE_EFFECT));
02298 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
02299 }
02300 visual_effect = callback;
02301 }
02302 }
02303
02304
02305 if (visual_effect == VE_DEFAULT ||
02306 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
02307
02308
02309 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
02310 if (visual_effect == VE_DEFAULT) {
02311 visual_effect = 1 << VE_DISABLE_EFFECT;
02312 } else {
02313 SetBit(visual_effect, VE_DISABLE_EFFECT);
02314 }
02315 } else {
02316 if (visual_effect == VE_DEFAULT) {
02317
02318 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
02319 }
02320 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
02321 }
02322 }
02323
02324 this->vcache.cached_vis_effect = visual_effect;
02325
02326 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
02327 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02328 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
02329 }
02330 }
02331
02332 static const int8 _vehicle_smoke_pos[8] = {
02333 1, 1, 1, 0, -1, -1, -1, 0
02334 };
02335
02340 void Vehicle::ShowVisualEffect() const
02341 {
02342 assert(this->IsPrimaryVehicle());
02343 bool sound = false;
02344
02345
02346
02347
02348
02349
02350 if (_settings_game.vehicle.smoke_amount == 0 ||
02351 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
02352 this->cur_speed < 2) {
02353 return;
02354 }
02355
02356 uint max_speed = this->vcache.cached_max_speed;
02357 if (this->type == VEH_TRAIN) {
02358 const Train *t = Train::From(this);
02359
02360
02361
02362
02363 if (HasBit(t->flags, VRF_REVERSING) ||
02364 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
02365 t->cur_speed >= t->Train::GetCurrentMaxSpeed())) {
02366 return;
02367 }
02368
02369 max_speed = min(max_speed, t->gcache.cached_max_track_speed);
02370 max_speed = min(max_speed, this->current_order.max_speed);
02371 }
02372 if (this->type == VEH_ROAD || this->type == VEH_SHIP) max_speed = min(max_speed, this->current_order.max_speed * 2);
02373
02374 const Vehicle *v = this;
02375
02376 do {
02377 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
02378 byte effect_type = GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
02379 bool disable_effect = HasBit(v->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
02380
02381
02382
02383
02384
02385
02386
02387
02388 if (disable_effect ||
02389 v->vehstatus & VS_HIDDEN ||
02390 (MayHaveBridgeAbove(v->tile) && IsBridgeAbove(v->tile)) ||
02391 IsDepotTile(v->tile) ||
02392 IsTunnelTile(v->tile) ||
02393 (v->type == VEH_TRAIN &&
02394 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
02395 continue;
02396 }
02397
02398
02399
02400
02401 if (v->type == VEH_TRAIN) effect_offset += (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
02402
02403 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
02404 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
02405
02406 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
02407 x = -x;
02408 y = -y;
02409 }
02410
02411 switch (effect_type) {
02412 case VE_TYPE_STEAM:
02413
02414
02415
02416
02417
02418 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / max_speed))) == 0) {
02419 CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
02420 sound = true;
02421 }
02422 break;
02423
02424 case VE_TYPE_DIESEL: {
02425
02426
02427
02428
02429
02430
02431
02432
02433
02434
02435
02436 int power_weight_effect = 0;
02437 if (v->type == VEH_TRAIN) {
02438 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
02439 }
02440 if (this->cur_speed < (max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
02441 Chance16((64 - ((this->cur_speed << 5) / max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
02442 CreateEffectVehicleRel(v, x, y, 10, EV_DIESEL_SMOKE);
02443 sound = true;
02444 }
02445 break;
02446 }
02447
02448 case VE_TYPE_ELECTRIC:
02449
02450
02451
02452
02453
02454
02455 if (GB(v->tick_counter, 0, 2) == 0 &&
02456 Chance16((6 - ((this->cur_speed << 2) / max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
02457 CreateEffectVehicleRel(v, x, y, 10, EV_ELECTRIC_SPARK);
02458 sound = true;
02459 }
02460 break;
02461
02462 default:
02463 break;
02464 }
02465 } while ((v = v->Next()) != NULL);
02466
02467 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
02468 }
02469
02474 void Vehicle::SetNext(Vehicle *next)
02475 {
02476 assert(this != next);
02477
02478 if (this->next != NULL) {
02479
02480 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02481 v->first = this->next;
02482 }
02483 this->next->previous = NULL;
02484 }
02485
02486 this->next = next;
02487
02488 if (this->next != NULL) {
02489
02490 if (this->next->previous != NULL) this->next->previous->next = NULL;
02491 this->next->previous = this;
02492 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02493 v->first = this->first;
02494 }
02495 }
02496 }
02497
02503 void Vehicle::AddToShared(Vehicle *shared_chain)
02504 {
02505 assert(this->previous_shared == NULL && this->next_shared == NULL);
02506
02507 if (shared_chain->orders.list == NULL) {
02508 assert(shared_chain->previous_shared == NULL);
02509 assert(shared_chain->next_shared == NULL);
02510 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
02511 }
02512
02513 this->next_shared = shared_chain->next_shared;
02514 this->previous_shared = shared_chain;
02515
02516 shared_chain->next_shared = this;
02517
02518 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
02519
02520 shared_chain->orders.list->AddVehicle(this);
02521 }
02522
02526 void Vehicle::RemoveFromShared()
02527 {
02528
02529
02530 bool were_first = (this->FirstShared() == this);
02531 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
02532
02533 this->orders.list->RemoveVehicle(this);
02534
02535 if (!were_first) {
02536
02537 this->previous_shared->next_shared = this->NextShared();
02538 }
02539
02540 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
02541
02542
02543 if (this->orders.list->GetNumVehicles() == 1) {
02544
02545 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
02546 InvalidateVehicleOrder(this->FirstShared(), 0);
02547 } else if (were_first) {
02548
02549
02550 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
02551 }
02552
02553 this->next_shared = NULL;
02554 this->previous_shared = NULL;
02555 }
02556
02557 void VehiclesYearlyLoop()
02558 {
02559 Vehicle *v;
02560 FOR_ALL_VEHICLES(v) {
02561 if (v->IsPrimaryVehicle()) {
02562
02563 Money profit = v->GetDisplayProfitThisYear();
02564 if (v->age >= 730 && profit < 0) {
02565 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
02566 SetDParam(0, v->index);
02567 SetDParam(1, profit);
02568 AddVehicleNewsItem(
02569 STR_NEWS_VEHICLE_IS_UNPROFITABLE,
02570 NS_ADVICE,
02571 v->index
02572 );
02573 }
02574 AI::NewEvent(v->owner, new ScriptEventVehicleUnprofitable(v->index));
02575 }
02576
02577 v->profit_last_year = v->profit_this_year;
02578 v->profit_this_year = 0;
02579 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
02580 }
02581 }
02582 GroupStatistics::UpdateProfits();
02583 SetWindowClassesDirty(WC_TRAINS_LIST);
02584 SetWindowClassesDirty(WC_SHIPS_LIST);
02585 SetWindowClassesDirty(WC_ROADVEH_LIST);
02586 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
02587 }
02588
02589
02599 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
02600 {
02601 const Engine *e = Engine::GetIfValid(engine_type);
02602 assert(e != NULL);
02603
02604 switch (e->type) {
02605 case VEH_TRAIN:
02606 return (st->facilities & FACIL_TRAIN) != 0;
02607
02608 case VEH_ROAD:
02609
02610
02611
02612 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
02613
02614 case VEH_SHIP:
02615 return (st->facilities & FACIL_DOCK) != 0;
02616
02617 case VEH_AIRCRAFT:
02618 return (st->facilities & FACIL_AIRPORT) != 0 &&
02619 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
02620
02621 default:
02622 return false;
02623 }
02624 }
02625
02632 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
02633 {
02634 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
02635
02636 return CanVehicleUseStation(v->engine_type, st);
02637 }
02638
02644 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
02645 {
02646 assert(this->IsGroundVehicle());
02647 if (this->type == VEH_TRAIN) {
02648 return &Train::From(this)->gcache;
02649 } else {
02650 return &RoadVehicle::From(this)->gcache;
02651 }
02652 }
02653
02659 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
02660 {
02661 assert(this->IsGroundVehicle());
02662 if (this->type == VEH_TRAIN) {
02663 return &Train::From(this)->gcache;
02664 } else {
02665 return &RoadVehicle::From(this)->gcache;
02666 }
02667 }
02668
02674 uint16 &Vehicle::GetGroundVehicleFlags()
02675 {
02676 assert(this->IsGroundVehicle());
02677 if (this->type == VEH_TRAIN) {
02678 return Train::From(this)->gv_flags;
02679 } else {
02680 return RoadVehicle::From(this)->gv_flags;
02681 }
02682 }
02683
02689 const uint16 &Vehicle::GetGroundVehicleFlags() const
02690 {
02691 assert(this->IsGroundVehicle());
02692 if (this->type == VEH_TRAIN) {
02693 return Train::From(this)->gv_flags;
02694 } else {
02695 return RoadVehicle::From(this)->gv_flags;
02696 }
02697 }
02698
02707 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
02708 {
02709 if (v->type == VEH_TRAIN) {
02710 Train *u = Train::From(v);
02711
02712 u = u->GetFirstEnginePart();
02713
02714
02715 for (; u != NULL && num_vehicles > 0; num_vehicles--) {
02716 do {
02717
02718 set.Include(u->index);
02719
02720
02721 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
02722
02723 u = u->Next();
02724 } while (u != NULL && u->IsArticulatedPart());
02725 }
02726 }
02727 }