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 #include "linkgraph/linkgraph.h"
00054 #include "linkgraph/refresh.h"
00055
00056 #include "table/strings.h"
00057
00058 #define GEN_HASH(x, y) ((GB((y), 6 + ZOOM_LVL_SHIFT, 6) << 6) + GB((x), 7 + ZOOM_LVL_SHIFT, 6))
00059
00060 VehicleID _new_vehicle_id;
00061 uint16 _returned_refit_capacity;
00062 uint16 _returned_mail_refit_capacity;
00063
00064
00066 VehiclePool _vehicle_pool("Vehicle");
00067 INSTANTIATE_POOL_METHODS(Vehicle)
00068
00069
00075 bool Vehicle::NeedsAutorenewing(const Company *c, bool use_renew_setting) const
00076 {
00077
00078
00079
00080
00081 assert(c == Company::Get(this->owner));
00082
00083 if (use_renew_setting && !c->settings.engine_renew) return false;
00084 if (this->age - this->max_age < (c->settings.engine_renew_months * 30)) return false;
00085
00086
00087 if (this->type == VEH_TRAIN && !Train::From(this)->IsEngine()) return false;
00088
00089 return true;
00090 }
00091
00097 void VehicleServiceInDepot(Vehicle *v)
00098 {
00099 assert(v != NULL);
00100 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
00101
00102 do {
00103 v->date_of_last_service = _date;
00104 v->breakdowns_since_last_service = 0;
00105 v->reliability = v->GetEngine()->reliability;
00106
00107 v->breakdown_chance /= 4;
00108 v = v->Next();
00109 } while (v != NULL && v->HasEngineType());
00110 }
00111
00118 bool Vehicle::NeedsServicing() const
00119 {
00120
00121
00122 if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
00123
00124
00125 const Company *c = Company::Get(this->owner);
00126 if (this->ServiceIntervalIsPercent() ?
00127 (this->reliability >= this->GetEngine()->reliability * (100 - this->GetServiceInterval()) / 100) :
00128 (this->date_of_last_service + this->GetServiceInterval() >= _date)) {
00129 return false;
00130 }
00131
00132
00133
00134 if (!_settings_game.order.no_servicing_if_no_breakdowns ||
00135 _settings_game.difficulty.vehicle_breakdowns != 0) {
00136 return true;
00137 }
00138
00139
00140
00141
00142 bool pending_replace = false;
00143 Money needed_money = c->settings.engine_renew_money;
00144 if (needed_money > c->money) return false;
00145
00146 for (const Vehicle *v = this; v != NULL; v = (v->type == VEH_TRAIN) ? Train::From(v)->GetNextUnit() : NULL) {
00147 bool replace_when_old = false;
00148 EngineID new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id, &replace_when_old);
00149
00150
00151 if (new_engine == INVALID_ENGINE || !HasBit(Engine::Get(new_engine)->company_avail, v->owner)) continue;
00152
00153 if (replace_when_old && !v->NeedsAutorenewing(c, false)) continue;
00154
00155
00156 uint32 available_cargo_types, union_mask;
00157 GetArticulatedRefitMasks(new_engine, true, &union_mask, &available_cargo_types);
00158
00159 if (union_mask != 0) {
00160 CargoID cargo_type;
00161
00162 if (IsArticulatedVehicleCarryingDifferentCargoes(v, &cargo_type)) continue;
00163
00164
00165 if (cargo_type != CT_INVALID) {
00166
00167 if (!HasBit(available_cargo_types, cargo_type)) continue;
00168 }
00169 }
00170
00171
00172
00173 pending_replace = true;
00174 needed_money += 2 * Engine::Get(new_engine)->GetCost();
00175 if (needed_money > c->money) return false;
00176 }
00177
00178 return pending_replace;
00179 }
00180
00186 bool Vehicle::NeedsAutomaticServicing() const
00187 {
00188 if (this->HasDepotOrder()) return false;
00189 if (this->current_order.IsType(OT_LOADING)) return false;
00190 if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
00191 return NeedsServicing();
00192 }
00193
00194 uint Vehicle::Crash(bool flooded)
00195 {
00196 assert((this->vehstatus & VS_CRASHED) == 0);
00197 assert(this->Previous() == NULL);
00198
00199 uint pass = 0;
00200
00201 if (this->IsPrimaryVehicle()) this->vehstatus |= VS_STOPPED;
00202
00203 for (Vehicle *v = this; v != NULL; v = v->Next()) {
00204
00205 if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo.TotalCount();
00206 v->vehstatus |= VS_CRASHED;
00207 MarkSingleVehicleDirty(v);
00208 }
00209
00210
00211 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00212 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
00213 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
00214 SetWindowDirty(WC_VEHICLE_DEPOT, this->tile);
00215
00216 delete this->cargo_payment;
00217 this->cargo_payment = NULL;
00218
00219 return RandomRange(pass + 1);
00220 }
00221
00222
00231 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
00232 {
00233 const Engine *e = Engine::Get(engine);
00234 GRFConfig *grfconfig = GetGRFConfig(e->GetGRFID());
00235
00236 if (!HasBit(grfconfig->grf_bugs, bug_type)) {
00237 SetBit(grfconfig->grf_bugs, bug_type);
00238 SetDParamStr(0, grfconfig->GetName());
00239 SetDParam(1, engine);
00240 ShowErrorMessage(part1, part2, WL_CRITICAL);
00241 if (!_networking) DoCommand(0, critical ? PM_PAUSED_ERROR : PM_PAUSED_NORMAL, 1, DC_EXEC, CMD_PAUSE);
00242 }
00243
00244
00245 char buffer[512];
00246
00247 SetDParamStr(0, grfconfig->GetName());
00248 GetString(buffer, part1, lastof(buffer));
00249 DEBUG(grf, 0, "%s", buffer + 3);
00250
00251 SetDParam(1, engine);
00252 GetString(buffer, part2, lastof(buffer));
00253 DEBUG(grf, 0, "%s", buffer + 3);
00254 }
00255
00261 void VehicleLengthChanged(const Vehicle *u)
00262 {
00263
00264 const Engine *engine = u->GetEngine();
00265 uint32 grfid = engine->grf_prop.grffile->grfid;
00266 GRFConfig *grfconfig = GetGRFConfig(grfid);
00267 if (GamelogGRFBugReverse(grfid, engine->grf_prop.local_id) || !HasBit(grfconfig->grf_bugs, GBUG_VEH_LENGTH)) {
00268 ShowNewGrfVehicleError(u->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_VEHICLE_LENGTH, GBUG_VEH_LENGTH, true);
00269 }
00270 }
00271
00276 Vehicle::Vehicle(VehicleType type)
00277 {
00278 this->type = type;
00279 this->coord.left = INVALID_COORD;
00280 this->group_id = DEFAULT_GROUP;
00281 this->fill_percent_te_id = INVALID_TE_ID;
00282 this->first = this;
00283 this->colourmap = PAL_NONE;
00284 this->cargo_age_counter = 1;
00285 this->last_station_visited = INVALID_STATION;
00286 this->last_loading_station = INVALID_STATION;
00287 }
00288
00293 byte VehicleRandomBits()
00294 {
00295 return GB(Random(), 0, 8);
00296 }
00297
00298
00299
00300 const int HASH_BITS = 7;
00301 const int HASH_SIZE = 1 << HASH_BITS;
00302 const int HASH_MASK = HASH_SIZE - 1;
00303 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
00304 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
00305
00306
00307
00308 const int HASH_RES = 0;
00309
00310 static Vehicle *_vehicle_tile_hash[TOTAL_HASH_SIZE];
00311
00312 static Vehicle *VehicleFromTileHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
00313 {
00314 for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
00315 for (int x = xl; ; x = (x + 1) & HASH_MASK) {
00316 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
00317 for (; v != NULL; v = v->hash_tile_next) {
00318 Vehicle *a = proc(v, data);
00319 if (find_first && a != NULL) return a;
00320 }
00321 if (x == xu) break;
00322 }
00323 if (y == yu) break;
00324 }
00325
00326 return NULL;
00327 }
00328
00329
00341 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
00342 {
00343 const int COLL_DIST = 6;
00344
00345
00346 int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00347 int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00348 int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00349 int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00350
00351 return VehicleFromTileHash(xl, yl, xu, yu, data, proc, find_first);
00352 }
00353
00368 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00369 {
00370 VehicleFromPosXY(x, y, data, proc, false);
00371 }
00372
00384 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00385 {
00386 return VehicleFromPosXY(x, y, data, proc, true) != NULL;
00387 }
00388
00399 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
00400 {
00401 int x = GB(TileX(tile), HASH_RES, HASH_BITS);
00402 int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
00403
00404 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
00405 for (; v != NULL; v = v->hash_tile_next) {
00406 if (v->tile != tile) continue;
00407
00408 Vehicle *a = proc(v, data);
00409 if (find_first && a != NULL) return a;
00410 }
00411
00412 return NULL;
00413 }
00414
00428 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00429 {
00430 VehicleFromPos(tile, data, proc, false);
00431 }
00432
00443 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00444 {
00445 return VehicleFromPos(tile, data, proc, true) != NULL;
00446 }
00447
00454 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
00455 {
00456 int z = *(int*)data;
00457
00458 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
00459 if (v->z_pos > z) return NULL;
00460
00461 return v;
00462 }
00463
00469 CommandCost EnsureNoVehicleOnGround(TileIndex tile)
00470 {
00471 int z = GetTileMaxPixelZ(tile);
00472
00473
00474
00475
00476
00477 Vehicle *v = VehicleFromPos(tile, &z, &EnsureNoVehicleProcZ, true);
00478 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00479 return CommandCost();
00480 }
00481
00483 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
00484 {
00485 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
00486 if (v == (const Vehicle *)data) return NULL;
00487
00488 return v;
00489 }
00490
00498 CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
00499 {
00500
00501
00502
00503
00504 Vehicle *v = VehicleFromPos(tile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
00505 if (v == NULL) v = VehicleFromPos(endtile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
00506
00507 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00508 return CommandCost();
00509 }
00510
00511 static Vehicle *EnsureNoTrainOnTrackProc(Vehicle *v, void *data)
00512 {
00513 TrackBits rail_bits = *(TrackBits *)data;
00514
00515 if (v->type != VEH_TRAIN) return NULL;
00516
00517 Train *t = Train::From(v);
00518 if ((t->track != rail_bits) && !TracksOverlap(t->track | rail_bits)) return NULL;
00519
00520 return v;
00521 }
00522
00531 CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
00532 {
00533
00534
00535
00536
00537 Vehicle *v = VehicleFromPos(tile, &track_bits, &EnsureNoTrainOnTrackProc, true);
00538 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00539 return CommandCost();
00540 }
00541
00542 static void UpdateVehicleTileHash(Vehicle *v, bool remove)
00543 {
00544 Vehicle **old_hash = v->hash_tile_current;
00545 Vehicle **new_hash;
00546
00547 if (remove) {
00548 new_hash = NULL;
00549 } else {
00550 int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
00551 int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
00552 new_hash = &_vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
00553 }
00554
00555 if (old_hash == new_hash) return;
00556
00557
00558 if (old_hash != NULL) {
00559 if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = v->hash_tile_prev;
00560 *v->hash_tile_prev = v->hash_tile_next;
00561 }
00562
00563
00564 if (new_hash != NULL) {
00565 v->hash_tile_next = *new_hash;
00566 if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = &v->hash_tile_next;
00567 v->hash_tile_prev = new_hash;
00568 *new_hash = v;
00569 }
00570
00571
00572 v->hash_tile_current = new_hash;
00573 }
00574
00575 static Vehicle *_vehicle_viewport_hash[0x1000];
00576
00577 static void UpdateVehicleViewportHash(Vehicle *v, int x, int y)
00578 {
00579 Vehicle **old_hash, **new_hash;
00580 int old_x = v->coord.left;
00581 int old_y = v->coord.top;
00582
00583 new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(x, y)];
00584 old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(old_x, old_y)];
00585
00586 if (old_hash == new_hash) return;
00587
00588
00589 if (old_hash != NULL) {
00590 if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = v->hash_viewport_prev;
00591 *v->hash_viewport_prev = v->hash_viewport_next;
00592 }
00593
00594
00595 if (new_hash != NULL) {
00596 v->hash_viewport_next = *new_hash;
00597 if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = &v->hash_viewport_next;
00598 v->hash_viewport_prev = new_hash;
00599 *new_hash = v;
00600 }
00601 }
00602
00603 void ResetVehicleHash()
00604 {
00605 Vehicle *v;
00606 FOR_ALL_VEHICLES(v) { v->hash_tile_current = NULL; }
00607 memset(_vehicle_viewport_hash, 0, sizeof(_vehicle_viewport_hash));
00608 memset(_vehicle_tile_hash, 0, sizeof(_vehicle_tile_hash));
00609 }
00610
00611 void ResetVehicleColourMap()
00612 {
00613 Vehicle *v;
00614 FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
00615 }
00616
00621 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
00622 static AutoreplaceMap _vehicles_to_autoreplace;
00623
00624 void InitializeVehicles()
00625 {
00626 _vehicles_to_autoreplace.Reset();
00627 ResetVehicleHash();
00628 }
00629
00630 uint CountVehiclesInChain(const Vehicle *v)
00631 {
00632 uint count = 0;
00633 do count++; while ((v = v->Next()) != NULL);
00634 return count;
00635 }
00636
00641 bool Vehicle::IsEngineCountable() const
00642 {
00643 switch (this->type) {
00644 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
00645 case VEH_TRAIN:
00646 return !this->IsArticulatedPart() &&
00647 !Train::From(this)->IsRearDualheaded();
00648 case VEH_ROAD: return RoadVehicle::From(this)->IsFrontEngine();
00649 case VEH_SHIP: return true;
00650 default: return false;
00651 }
00652 }
00653
00658 bool Vehicle::HasEngineType() const
00659 {
00660 switch (this->type) {
00661 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
00662 case VEH_TRAIN:
00663 case VEH_ROAD:
00664 case VEH_SHIP: return true;
00665 default: return false;
00666 }
00667 }
00668
00674 const Engine *Vehicle::GetEngine() const
00675 {
00676 return Engine::Get(this->engine_type);
00677 }
00678
00684 const GRFFile *Vehicle::GetGRF() const
00685 {
00686 return this->GetEngine()->GetGRF();
00687 }
00688
00694 uint32 Vehicle::GetGRFID() const
00695 {
00696 return this->GetEngine()->GetGRFID();
00697 }
00698
00706 void Vehicle::HandlePathfindingResult(bool path_found)
00707 {
00708 if (path_found) {
00709
00710 if (!HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00711
00712
00713 ClrBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00714
00715 DeleteVehicleNews(this->index, STR_NEWS_VEHICLE_IS_LOST);
00716 return;
00717 }
00718
00719
00720 if (HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00721
00722
00723 SetBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00724
00725 AI::NewEvent(this->owner, new ScriptEventVehicleLost(this->index));
00726 if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) {
00727 SetDParam(0, this->index);
00728 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_LOST, this->index);
00729 }
00730 }
00731
00733 void Vehicle::PreDestructor()
00734 {
00735 if (CleaningPool()) return;
00736
00737 if (Station::IsValidID(this->last_station_visited)) {
00738 Station *st = Station::Get(this->last_station_visited);
00739 st->loading_vehicles.remove(this);
00740
00741 HideFillingPercent(&this->fill_percent_te_id);
00742 this->CancelReservation(INVALID_STATION, st);
00743 delete this->cargo_payment;
00744 }
00745
00746 if (this->IsEngineCountable()) {
00747 GroupStatistics::CountEngine(this, -1);
00748 if (this->IsPrimaryVehicle()) GroupStatistics::CountVehicle(this, -1);
00749 GroupStatistics::UpdateAutoreplace(this->owner);
00750
00751 if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
00752 DeleteGroupHighlightOfVehicle(this);
00753 }
00754
00755 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
00756 Aircraft *a = Aircraft::From(this);
00757 Station *st = GetTargetAirportIfValid(a);
00758 if (st != NULL) {
00759 const AirportFTA *layout = st->airport.GetFTA()->layout;
00760 CLRBITS(st->airport.flags, layout[a->previous_pos].block | layout[a->pos].block);
00761 }
00762 }
00763
00764
00765 if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
00766 RoadVehicle *v = RoadVehicle::From(this);
00767 if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
00768
00769 RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
00770 }
00771 }
00772
00773 if (this->Previous() == NULL) {
00774 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
00775 }
00776
00777 if (this->IsPrimaryVehicle()) {
00778 DeleteWindowById(WC_VEHICLE_VIEW, this->index);
00779 DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
00780 DeleteWindowById(WC_VEHICLE_REFIT, this->index);
00781 DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
00782 DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
00783 SetWindowDirty(WC_COMPANY, this->owner);
00784 OrderBackup::ClearVehicle(this);
00785 }
00786 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00787
00788 this->cargo.Truncate();
00789 DeleteVehicleOrders(this);
00790 DeleteDepotHighlightOfVehicle(this);
00791
00792 extern void StopGlobalFollowVehicle(const Vehicle *v);
00793 StopGlobalFollowVehicle(this);
00794
00795 ReleaseDisastersTargetingVehicle(this->index);
00796 }
00797
00798 Vehicle::~Vehicle()
00799 {
00800 if (CleaningPool()) {
00801 this->cargo.OnCleanPool();
00802 return;
00803 }
00804
00805
00806
00807 if (!(this->vehstatus & VS_HIDDEN)) MarkSingleVehicleDirty(this);
00808
00809 Vehicle *v = this->Next();
00810 this->SetNext(NULL);
00811
00812 delete v;
00813
00814 UpdateVehicleTileHash(this, true);
00815 UpdateVehicleViewportHash(this, INVALID_COORD, 0);
00816 DeleteVehicleNews(this->index, INVALID_STRING_ID);
00817 DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index);
00818 }
00819
00824 void VehicleEnteredDepotThisTick(Vehicle *v)
00825 {
00826
00827 _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
00828
00829
00830
00831
00832
00833
00834 v->vehstatus |= VS_STOPPED;
00835 }
00836
00842 static void RunVehicleDayProc()
00843 {
00844 if (_game_mode != GM_NORMAL) return;
00845
00846
00847 for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
00848 Vehicle *v = Vehicle::Get(i);
00849 if (v == NULL) continue;
00850
00851
00852 if ((v->day_counter & 0x1F) == 0 && v->HasEngineType()) {
00853 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
00854 if (callback != CALLBACK_FAILED) {
00855 if (HasBit(callback, 0)) {
00856 TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32);
00857 }
00858
00859
00860
00861 if (callback != 0) v->First()->MarkDirty();
00862
00863 if (callback & ~3) ErrorUnknownCallbackResult(v->GetGRFID(), CBID_VEHICLE_32DAY_CALLBACK, callback);
00864 }
00865 }
00866
00867
00868 v->OnNewDay();
00869 }
00870 }
00871
00872 void CallVehicleTicks()
00873 {
00874 _vehicles_to_autoreplace.Clear();
00875
00876 RunVehicleDayProc();
00877
00878 Station *st;
00879 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
00880
00881 Vehicle *v;
00882 FOR_ALL_VEHICLES(v) {
00883
00884 if (!v->Tick()) {
00885 assert(Vehicle::Get(vehicle_index) == NULL);
00886 continue;
00887 }
00888
00889 assert(Vehicle::Get(vehicle_index) == v);
00890
00891 switch (v->type) {
00892 default: break;
00893
00894 case VEH_TRAIN:
00895 case VEH_ROAD:
00896 case VEH_AIRCRAFT:
00897 case VEH_SHIP: {
00898 Vehicle *front = v->First();
00899
00900 if (v->vcache.cached_cargo_age_period != 0) {
00901 v->cargo_age_counter = min(v->cargo_age_counter, v->vcache.cached_cargo_age_period);
00902 if (--v->cargo_age_counter == 0) {
00903 v->cargo.AgeCargo();
00904 v->cargo_age_counter = v->vcache.cached_cargo_age_period;
00905 }
00906 }
00907
00908
00909 if (front->vehstatus & VS_CRASHED) continue;
00910
00911
00912 if (v->vehstatus & VS_HIDDEN) continue;
00913
00914
00915 if ((front->vehstatus & VS_STOPPED) && (front->type != VEH_TRAIN || front->cur_speed == 0)) continue;
00916
00917
00918 switch (v->type) {
00919 case VEH_TRAIN:
00920 if (Train::From(v)->IsWagon()) continue;
00921 break;
00922
00923 case VEH_ROAD:
00924 if (!RoadVehicle::From(v)->IsFrontEngine()) continue;
00925 break;
00926
00927 case VEH_AIRCRAFT:
00928 if (!Aircraft::From(v)->IsNormalAircraft()) continue;
00929 break;
00930
00931 default:
00932 break;
00933 }
00934
00935 v->motion_counter += front->cur_speed;
00936
00937 if (GB(v->motion_counter, 0, 8) < front->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
00938
00939
00940 if (GB(v->tick_counter, 0, 4) == 0) {
00941
00942 bool running = (front->cur_speed > 0) && !(front->vehstatus & (VS_STOPPED | VS_TRAIN_SLOWING));
00943 PlayVehicleSound(v, running ? VSE_RUNNING_16 : VSE_STOPPED_16);
00944 }
00945
00946 break;
00947 }
00948 }
00949 }
00950
00951 Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
00952 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
00953 v = it->first;
00954
00955 cur_company.Change(v->owner);
00956
00957
00958
00959
00960 if (it->second) v->vehstatus &= ~VS_STOPPED;
00961
00962
00963 int x = v->x_pos;
00964 int y = v->y_pos;
00965 int z = v->z_pos;
00966
00967 const Company *c = Company::Get(_current_company);
00968 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
00969 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
00970 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
00971
00972 if (!IsLocalCompany()) continue;
00973
00974 if (res.Succeeded()) {
00975 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
00976 continue;
00977 }
00978
00979 StringID error_message = res.GetErrorMessage();
00980 if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
00981
00982 if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
00983
00984 StringID message;
00985 if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
00986 message = error_message;
00987 } else {
00988 message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
00989 }
00990
00991 SetDParam(0, v->index);
00992 SetDParam(1, error_message);
00993 AddVehicleAdviceNewsItem(message, v->index);
00994 }
00995
00996 cur_company.Restore();
00997 }
00998
01003 static void DoDrawVehicle(const Vehicle *v)
01004 {
01005 SpriteID image = v->cur_image;
01006 PaletteID pal = PAL_NONE;
01007
01008 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
01009
01010
01011 bool shadowed = (v->vehstatus & VS_SHADOW) != 0;
01012
01013 if (v->type == VEH_EFFECT) {
01014
01015
01016 TransparencyOption to = EffectVehicle::From(v)->GetTransparencyOption();
01017 if (to != TO_INVALID && (IsTransparencySet(to) || IsInvisibilitySet(to))) return;
01018 }
01019
01020 AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
01021 v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs);
01022 }
01023
01028 void ViewportAddVehicles(DrawPixelInfo *dpi)
01029 {
01030
01031 const int l = dpi->left;
01032 const int r = dpi->left + dpi->width;
01033 const int t = dpi->top;
01034 const int b = dpi->top + dpi->height;
01035
01036
01037 int xl, xu, yl, yu;
01038
01039 if (dpi->width + (70 * ZOOM_LVL_BASE) < (1 << (7 + 6 + ZOOM_LVL_SHIFT))) {
01040 xl = GB(l - (70 * ZOOM_LVL_BASE), 7 + ZOOM_LVL_SHIFT, 6);
01041 xu = GB(r, 7 + ZOOM_LVL_SHIFT, 6);
01042 } else {
01043
01044 xl = 0;
01045 xu = 0x3F;
01046 }
01047
01048 if (dpi->height + (70 * ZOOM_LVL_BASE) < (1 << (6 + 6 + ZOOM_LVL_SHIFT))) {
01049 yl = GB(t - (70 * ZOOM_LVL_BASE), 6 + ZOOM_LVL_SHIFT, 6) << 6;
01050 yu = GB(b, 6 + ZOOM_LVL_SHIFT, 6) << 6;
01051 } else {
01052
01053 yl = 0;
01054 yu = 0x3F << 6;
01055 }
01056
01057 for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
01058 for (int x = xl;; x = (x + 1) & 0x3F) {
01059 const Vehicle *v = _vehicle_viewport_hash[x + y];
01060
01061 while (v != NULL) {
01062 if (!(v->vehstatus & VS_HIDDEN) &&
01063 l <= v->coord.right &&
01064 t <= v->coord.bottom &&
01065 r >= v->coord.left &&
01066 b >= v->coord.top) {
01067 DoDrawVehicle(v);
01068 }
01069 v = v->hash_viewport_next;
01070 }
01071
01072 if (x == xu) break;
01073 }
01074
01075 if (y == yu) break;
01076 }
01077 }
01078
01086 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
01087 {
01088 Vehicle *found = NULL, *v;
01089 uint dist, best_dist = UINT_MAX;
01090
01091 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
01092
01093 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
01094 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
01095
01096 FOR_ALL_VEHICLES(v) {
01097 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
01098 x >= v->coord.left && x <= v->coord.right &&
01099 y >= v->coord.top && y <= v->coord.bottom) {
01100
01101 dist = max(
01102 abs(((v->coord.left + v->coord.right) >> 1) - x),
01103 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
01104 );
01105
01106 if (dist < best_dist) {
01107 found = v;
01108 best_dist = dist;
01109 }
01110 }
01111 }
01112
01113 return found;
01114 }
01115
01120 void DecreaseVehicleValue(Vehicle *v)
01121 {
01122 v->value -= v->value >> 8;
01123 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01124 }
01125
01126 static const byte _breakdown_chance[64] = {
01127 3, 3, 3, 3, 3, 3, 3, 3,
01128 4, 4, 5, 5, 6, 6, 7, 7,
01129 8, 8, 9, 9, 10, 10, 11, 11,
01130 12, 13, 13, 13, 13, 14, 15, 16,
01131 17, 19, 21, 25, 28, 31, 34, 37,
01132 40, 44, 48, 52, 56, 60, 64, 68,
01133 72, 80, 90, 100, 110, 120, 130, 140,
01134 150, 170, 190, 210, 230, 250, 250, 250,
01135 };
01136
01137 void CheckVehicleBreakdown(Vehicle *v)
01138 {
01139 int rel, rel_old;
01140
01141
01142 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
01143 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01144
01145 if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
01146 _settings_game.difficulty.vehicle_breakdowns < 1 ||
01147 v->cur_speed < 5 || _game_mode == GM_MENU) {
01148 return;
01149 }
01150
01151 uint32 r = Random();
01152
01153
01154 int chance = v->breakdown_chance + 1;
01155 if (Chance16I(1, 25, r)) chance += 25;
01156 v->breakdown_chance = min(255, chance);
01157
01158
01159 rel = v->reliability;
01160 if (v->type == VEH_SHIP) rel += 0x6666;
01161
01162
01163 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
01164
01165
01166 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
01167 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
01168 v->breakdown_delay = GB(r, 24, 7) + 0x80;
01169 v->breakdown_chance = 0;
01170 }
01171 }
01172
01179 bool Vehicle::HandleBreakdown()
01180 {
01181
01182
01183
01184
01185
01186 switch (this->breakdown_ctr) {
01187 case 0:
01188 return false;
01189
01190 case 2:
01191 this->breakdown_ctr = 1;
01192
01193 if (this->breakdowns_since_last_service != 255) {
01194 this->breakdowns_since_last_service++;
01195 }
01196
01197 if (this->type == VEH_AIRCRAFT) {
01198
01199 this->vehstatus |= VS_AIRCRAFT_BROKEN;
01200 } else {
01201 this->cur_speed = 0;
01202
01203 if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
01204 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
01205 (this->type == VEH_TRAIN ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
01206 (this->type == VEH_TRAIN ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
01207 }
01208
01209 if (!(this->vehstatus & VS_HIDDEN) && !HasBit(EngInfo(this->engine_type)->misc_flags, EF_NO_BREAKDOWN_SMOKE)) {
01210 EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
01211 if (u != NULL) u->animation_state = this->breakdown_delay * 2;
01212 }
01213 }
01214
01215 this->MarkDirty();
01216 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01217 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01218
01219
01220 case 1:
01221
01222 if (this->type == VEH_AIRCRAFT) return false;
01223
01224
01225 if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
01226 if (--this->breakdown_delay == 0) {
01227 this->breakdown_ctr = 0;
01228 this->MarkDirty();
01229 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01230 }
01231 }
01232 return true;
01233
01234 default:
01235 if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
01236 return false;
01237 }
01238 }
01239
01244 void AgeVehicle(Vehicle *v)
01245 {
01246 if (v->age < MAX_DAY) {
01247 v->age++;
01248 if (v->IsPrimaryVehicle() && v->age == VEHICLE_PROFIT_MIN_AGE + 1) GroupStatistics::VehicleReachedProfitAge(v);
01249 }
01250
01251 if (!v->IsPrimaryVehicle() && (v->type != VEH_TRAIN || !Train::From(v)->IsEngine())) return;
01252
01253 int age = v->age - v->max_age;
01254 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
01255 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
01256 v->reliability_spd_dec <<= 1;
01257 }
01258
01259 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01260
01261
01262 if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
01263
01264
01265 if (Company::Get(v->owner)->settings.engine_renew && v->GetEngine()->company_avail != 0) return;
01266
01267 StringID str;
01268 if (age == -DAYS_IN_LEAP_YEAR) {
01269 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
01270 } else if (age == 0) {
01271 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
01272 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
01273 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
01274 } else {
01275 return;
01276 }
01277
01278 SetDParam(0, v->index);
01279 AddVehicleAdviceNewsItem(str, v->index);
01280 }
01281
01288 uint8 CalcPercentVehicleFilled(const Vehicle *front, StringID *colour)
01289 {
01290 int count = 0;
01291 int max = 0;
01292 int cars = 0;
01293 int unloading = 0;
01294 bool loading = false;
01295
01296 bool is_loading = front->current_order.IsType(OT_LOADING);
01297
01298
01299 const Station *st = Station::GetIfValid(front->last_station_visited);
01300 assert(colour == NULL || (st != NULL && is_loading));
01301
01302 bool order_no_load = is_loading && (front->current_order.GetLoadType() & OLFB_NO_LOAD);
01303 bool order_full_load = is_loading && (front->current_order.GetLoadType() & OLFB_FULL_LOAD);
01304
01305
01306 for (const Vehicle *v = front; v != NULL; v = v->Next()) {
01307 count += v->cargo.StoredCount();
01308 max += v->cargo_cap;
01309 if (v->cargo_cap != 0 && colour != NULL) {
01310 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
01311 loading |= !order_no_load &&
01312 (order_full_load || st->goods[v->cargo_type].HasRating()) &&
01313 !HasBit(v->vehicle_flags, VF_LOADING_FINISHED) && !HasBit(v->vehicle_flags, VF_STOP_LOADING);
01314 cars++;
01315 }
01316 }
01317
01318 if (colour != NULL) {
01319 if (unloading == 0 && loading) {
01320 *colour = STR_PERCENT_UP;
01321 } else if (unloading == 0 && !loading) {
01322 *colour = STR_PERCENT_NONE;
01323 } else if (cars == unloading || !loading) {
01324 *colour = STR_PERCENT_DOWN;
01325 } else {
01326 *colour = STR_PERCENT_UP_DOWN;
01327 }
01328 }
01329
01330
01331 if (max == 0) return 100;
01332
01333
01334 return (count * 100) / max;
01335 }
01336
01341 void VehicleEnterDepot(Vehicle *v)
01342 {
01343
01344 assert(v == v->First());
01345
01346 switch (v->type) {
01347 case VEH_TRAIN: {
01348 Train *t = Train::From(v);
01349 SetWindowClassesDirty(WC_TRAINS_LIST);
01350
01351 SetDepotReservation(t->tile, false);
01352 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
01353
01354 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
01355 t->wait_counter = 0;
01356 t->force_proceed = TFP_NONE;
01357 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
01358 t->ConsistChanged(true);
01359 break;
01360 }
01361
01362 case VEH_ROAD:
01363 SetWindowClassesDirty(WC_ROADVEH_LIST);
01364 break;
01365
01366 case VEH_SHIP: {
01367 SetWindowClassesDirty(WC_SHIPS_LIST);
01368 Ship *ship = Ship::From(v);
01369 ship->state = TRACK_BIT_DEPOT;
01370 ship->UpdateCache();
01371 ship->UpdateViewport(true, true);
01372 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01373 break;
01374 }
01375
01376 case VEH_AIRCRAFT:
01377 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01378 HandleAircraftEnterHangar(Aircraft::From(v));
01379 break;
01380 default: NOT_REACHED();
01381 }
01382 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01383
01384 if (v->type != VEH_TRAIN) {
01385
01386
01387 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01388 }
01389 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01390
01391 v->vehstatus |= VS_HIDDEN;
01392 v->cur_speed = 0;
01393
01394 VehicleServiceInDepot(v);
01395
01396
01397 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01398 v->MarkDirty();
01399
01400 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01401 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01402
01403 const Order *real_order = v->GetOrder(v->cur_real_order_index);
01404 Order t = v->current_order;
01405 v->current_order.MakeDummy();
01406
01407
01408
01409 if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
01410 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
01411 (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
01412
01413 return;
01414 }
01415
01416 if (t.IsRefit()) {
01417 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01418 CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | 0xFF << 8, DC_EXEC, GetCmdRefitVeh(v));
01419 cur_company.Restore();
01420
01421 if (cost.Failed()) {
01422 _vehicles_to_autoreplace[v] = false;
01423 if (v->owner == _local_company) {
01424
01425 SetDParam(0, v->index);
01426 AddVehicleAdviceNewsItem(STR_NEWS_ORDER_REFIT_FAILED, v->index);
01427 }
01428 } else if (cost.GetCost() != 0) {
01429 v->profit_this_year -= cost.GetCost() << 8;
01430 if (v->owner == _local_company) {
01431 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01432 }
01433 }
01434 }
01435
01436 if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01437
01438 v->DeleteUnreachedImplicitOrders();
01439 UpdateVehicleTimetable(v, true);
01440 v->IncrementImplicitOrderIndex();
01441 }
01442 if (t.GetDepotActionType() & ODATFB_HALT) {
01443
01444 _vehicles_to_autoreplace[v] = false;
01445
01446
01447
01448 v->last_loading_station = INVALID_STATION;
01449 if (v->owner == _local_company) {
01450 SetDParam(0, v->index);
01451 AddVehicleAdviceNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, v->index);
01452 }
01453 AI::NewEvent(v->owner, new ScriptEventVehicleWaitingInDepot(v->index));
01454 }
01455 }
01456 }
01457
01458
01464 void VehicleUpdatePosition(Vehicle *v)
01465 {
01466 UpdateVehicleTileHash(v, false);
01467 }
01468
01475 void VehicleUpdateViewport(Vehicle *v, bool dirty)
01476 {
01477 int img = v->cur_image;
01478 Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
01479 const Sprite *spr = GetSprite(img, ST_NORMAL);
01480
01481 pt.x += spr->x_offs;
01482 pt.y += spr->y_offs;
01483
01484 UpdateVehicleViewportHash(v, pt.x, pt.y);
01485
01486 Rect old_coord = v->coord;
01487 v->coord.left = pt.x;
01488 v->coord.top = pt.y;
01489 v->coord.right = pt.x + spr->width + 2 * ZOOM_LVL_BASE;
01490 v->coord.bottom = pt.y + spr->height + 2 * ZOOM_LVL_BASE;
01491
01492 if (dirty) {
01493 if (old_coord.left == INVALID_COORD) {
01494 MarkSingleVehicleDirty(v);
01495 } else {
01496 MarkAllViewportsDirty(
01497 min(old_coord.left, v->coord.left),
01498 min(old_coord.top, v->coord.top),
01499 max(old_coord.right, v->coord.right) + 1 * ZOOM_LVL_BASE,
01500 max(old_coord.bottom, v->coord.bottom) + 1 * ZOOM_LVL_BASE
01501 );
01502 }
01503 }
01504 }
01505
01510 void VehicleUpdatePositionAndViewport(Vehicle *v)
01511 {
01512 VehicleUpdatePosition(v);
01513 VehicleUpdateViewport(v, true);
01514 }
01515
01520 void MarkSingleVehicleDirty(const Vehicle *v)
01521 {
01522 MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1 * ZOOM_LVL_BASE, v->coord.bottom + 1 * ZOOM_LVL_BASE);
01523 }
01524
01530 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01531 {
01532 static const int8 _delta_coord[16] = {
01533 -1,-1,-1, 0, 1, 1, 1, 0,
01534 -1, 0, 1, 1, 1, 0,-1,-1,
01535 };
01536
01537 int x = v->x_pos + _delta_coord[v->direction];
01538 int y = v->y_pos + _delta_coord[v->direction + 8];
01539
01540 GetNewVehiclePosResult gp;
01541 gp.x = x;
01542 gp.y = y;
01543 gp.old_tile = v->tile;
01544 gp.new_tile = TileVirtXY(x, y);
01545 return gp;
01546 }
01547
01548 static const Direction _new_direction_table[] = {
01549 DIR_N, DIR_NW, DIR_W,
01550 DIR_NE, DIR_SE, DIR_SW,
01551 DIR_E, DIR_SE, DIR_S
01552 };
01553
01554 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01555 {
01556 int i = 0;
01557
01558 if (y >= v->y_pos) {
01559 if (y != v->y_pos) i += 3;
01560 i += 3;
01561 }
01562
01563 if (x >= v->x_pos) {
01564 if (x != v->x_pos) i++;
01565 i++;
01566 }
01567
01568 Direction dir = v->direction;
01569
01570 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01571 if (dirdiff == DIRDIFF_SAME) return dir;
01572 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01573 }
01574
01584 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01585 {
01586 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01587 }
01588
01596 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01597 {
01598
01599 const Vehicle *v;
01600 FOR_ALL_VEHICLES(v) {
01601 if (v->type == type && v->owner == owner) {
01602 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01603 }
01604 }
01605
01606 if (this->maxid == 0) return;
01607
01608
01609
01610
01611 this->cache = CallocT<bool>(this->maxid + 2);
01612
01613
01614 FOR_ALL_VEHICLES(v) {
01615 if (v->type == type && v->owner == owner) {
01616 this->cache[v->unitnumber] = true;
01617 }
01618 }
01619 }
01620
01622 UnitID FreeUnitIDGenerator::NextID()
01623 {
01624 if (this->maxid <= this->curid) return ++this->curid;
01625
01626 while (this->cache[++this->curid]) { }
01627
01628 return this->curid;
01629 }
01630
01636 UnitID GetFreeUnitNumber(VehicleType type)
01637 {
01638
01639 uint max_veh;
01640 switch (type) {
01641 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
01642 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
01643 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
01644 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
01645 default: NOT_REACHED();
01646 }
01647
01648 const Company *c = Company::Get(_current_company);
01649 if (c->group_all[type].num_vehicle >= max_veh) return UINT16_MAX;
01650
01651 FreeUnitIDGenerator gen(type, _current_company);
01652
01653 return gen.NextID();
01654 }
01655
01656
01665 bool CanBuildVehicleInfrastructure(VehicleType type)
01666 {
01667 assert(IsCompanyBuildableVehicleType(type));
01668
01669 if (!Company::IsValidID(_local_company)) return false;
01670 if (!_settings_client.gui.disable_unsuitable_building) return true;
01671
01672 UnitID max;
01673 switch (type) {
01674 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
01675 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
01676 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
01677 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01678 default: NOT_REACHED();
01679 }
01680
01681
01682 if (max > 0) {
01683
01684 const Engine *e;
01685 FOR_ALL_ENGINES_OF_TYPE(e, type) {
01686 if (HasBit(e->company_avail, _local_company)) return true;
01687 }
01688 return false;
01689 }
01690
01691
01692 const Vehicle *v;
01693 FOR_ALL_VEHICLES(v) {
01694 if (v->owner == _local_company && v->type == type) return true;
01695 }
01696
01697 return false;
01698 }
01699
01700
01708 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
01709 {
01710 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01711 const Engine *e = Engine::Get(engine_type);
01712 switch (e->type) {
01713 default: NOT_REACHED();
01714 case VEH_TRAIN:
01715 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
01716
01717
01718 engine_type = parent_engine_type;
01719 e = Engine::Get(engine_type);
01720
01721 }
01722
01723 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01724 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01725 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
01726 if (!CargoSpec::Get(cargo_type)->is_freight) {
01727 if (parent_engine_type == INVALID_ENGINE) {
01728 return LS_PASSENGER_WAGON_STEAM;
01729 } else {
01730 switch (RailVehInfo(parent_engine_type)->engclass) {
01731 default: NOT_REACHED();
01732 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
01733 case EC_DIESEL: return LS_PASSENGER_WAGON_DIESEL;
01734 case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
01735 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
01736 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
01737 }
01738 }
01739 } else {
01740 return LS_FREIGHT_WAGON;
01741 }
01742 } else {
01743 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
01744
01745 switch (e->u.rail.engclass) {
01746 default: NOT_REACHED();
01747 case EC_STEAM: return LS_STEAM;
01748 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
01749 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
01750 case EC_MONORAIL: return LS_MONORAIL;
01751 case EC_MAGLEV: return LS_MAGLEV;
01752 }
01753 }
01754
01755 case VEH_ROAD:
01756
01757 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01758 engine_type = parent_engine_type;
01759 e = Engine::Get(engine_type);
01760 cargo_type = v->First()->cargo_type;
01761 }
01762 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01763 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01764
01765
01766 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
01767
01768 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01769 } else {
01770
01771 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01772 }
01773
01774 case VEH_SHIP:
01775 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01776 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01777 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01778
01779 case VEH_AIRCRAFT:
01780 switch (e->u.air.subtype) {
01781 case AIR_HELI: return LS_HELICOPTER;
01782 case AIR_CTOL: return LS_SMALL_PLANE;
01783 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
01784 default: NOT_REACHED();
01785 }
01786 }
01787 }
01788
01798 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
01799 {
01800 const Company *c = Company::Get(company);
01801 LiveryScheme scheme = LS_DEFAULT;
01802
01803
01804
01805 if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
01806
01807 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
01808
01809
01810 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01811 }
01812
01813 return &c->livery[scheme];
01814 }
01815
01816
01817 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01818 {
01819 PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01820
01821
01822 if (map != PAL_NONE) return map;
01823
01824 const Engine *e = Engine::Get(engine_type);
01825
01826
01827 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
01828 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01829
01830 if (callback != CALLBACK_FAILED) {
01831 assert_compile(PAL_NONE == 0);
01832 map = GB(callback, 0, 14);
01833
01834
01835 if (!HasBit(callback, 14)) {
01836
01837 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01838 return map;
01839 }
01840 }
01841 }
01842
01843 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
01844
01845 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
01846
01847
01848 if (!Company::IsValidID(company)) return map;
01849
01850 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
01851
01852 map += livery->colour1;
01853 if (twocc) map += livery->colour2 * 16;
01854
01855
01856 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01857 return map;
01858 }
01859
01866 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
01867 {
01868 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01869 }
01870
01876 PaletteID GetVehiclePalette(const Vehicle *v)
01877 {
01878 if (v->IsGroundVehicle()) {
01879 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
01880 }
01881
01882 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01883 }
01884
01888 void Vehicle::DeleteUnreachedImplicitOrders()
01889 {
01890 if (this->IsGroundVehicle()) {
01891 uint16 &gv_flags = this->GetGroundVehicleFlags();
01892 if (HasBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)) {
01893
01894 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
01895 this->cur_implicit_order_index = this->cur_real_order_index;
01896 InvalidateVehicleOrder(this, 0);
01897 return;
01898 }
01899 }
01900
01901 const Order *order = this->GetOrder(this->cur_implicit_order_index);
01902 while (order != NULL) {
01903 if (this->cur_implicit_order_index == this->cur_real_order_index) break;
01904
01905 if (order->IsType(OT_IMPLICIT)) {
01906 DeleteOrder(this, this->cur_implicit_order_index);
01907
01908 order = this->GetOrder(this->cur_implicit_order_index);
01909 } else {
01910
01911 order = order->next;
01912 this->cur_implicit_order_index++;
01913 }
01914
01915
01916 if (order == NULL) {
01917 order = this->GetOrder(0);
01918 this->cur_implicit_order_index = 0;
01919 }
01920 }
01921 }
01922
01927 void Vehicle::BeginLoading()
01928 {
01929 assert(IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP);
01930
01931 if (this->current_order.IsType(OT_GOTO_STATION) &&
01932 this->current_order.GetDestination() == this->last_station_visited) {
01933 this->DeleteUnreachedImplicitOrders();
01934
01935
01936 this->current_order.MakeLoading(true);
01937 UpdateVehicleTimetable(this, true);
01938
01939
01940
01941
01942
01943
01944 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
01945
01946 } else {
01947
01948
01949
01950
01951 Order *in_list = this->GetOrder(this->cur_implicit_order_index);
01952 if (this->IsGroundVehicle() &&
01953 (in_list == NULL || !in_list->IsType(OT_IMPLICIT) ||
01954 in_list->GetDestination() != this->last_station_visited)) {
01955 bool suppress_implicit_orders = HasBit(this->GetGroundVehicleFlags(), GVF_SUPPRESS_IMPLICIT_ORDERS);
01956
01957 Order *prev_order = this->cur_implicit_order_index > 0 ? this->GetOrder(this->cur_implicit_order_index - 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : NULL);
01958 if (prev_order == NULL ||
01959 (!prev_order->IsType(OT_IMPLICIT) && !prev_order->IsType(OT_GOTO_STATION)) ||
01960 prev_order->GetDestination() != this->last_station_visited) {
01961
01962
01963
01964
01965
01966
01967 int target_index = this->cur_implicit_order_index;
01968 bool found = false;
01969 while (target_index != this->cur_real_order_index || this->GetNumManualOrders() == 0) {
01970 const Order *order = this->GetOrder(target_index);
01971 if (order == NULL) break;
01972 if (order->IsType(OT_IMPLICIT) && order->GetDestination() == this->last_station_visited) {
01973 found = true;
01974 break;
01975 }
01976 target_index++;
01977 if (target_index >= this->orders.list->GetNumOrders()) {
01978 if (this->GetNumManualOrders() == 0 &&
01979 this->GetNumOrders() < IMPLICIT_ORDER_ONLY_CAP) {
01980 break;
01981 }
01982 target_index = 0;
01983 }
01984 if (target_index == this->cur_implicit_order_index) break;
01985 }
01986
01987 if (found) {
01988 if (suppress_implicit_orders) {
01989
01990 this->cur_implicit_order_index = target_index;
01991 InvalidateVehicleOrder(this, 0);
01992 } else {
01993
01994 const Order *order = this->GetOrder(this->cur_implicit_order_index);
01995 while (!order->IsType(OT_IMPLICIT) || order->GetDestination() != this->last_station_visited) {
01996 if (order->IsType(OT_IMPLICIT)) {
01997 DeleteOrder(this, this->cur_implicit_order_index);
01998
01999 order = this->GetOrder(this->cur_implicit_order_index);
02000 } else {
02001
02002 order = order->next;
02003 this->cur_implicit_order_index++;
02004 }
02005
02006
02007 if (order == NULL) {
02008 order = this->GetOrder(0);
02009 this->cur_implicit_order_index = 0;
02010 }
02011 assert(order != NULL);
02012 }
02013 }
02014 } else if (!suppress_implicit_orders &&
02015 ((this->orders.list == NULL ? OrderList::CanAllocateItem() : this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID)) &&
02016 Order::CanAllocateItem()) {
02017
02018 Order *implicit_order = new Order();
02019 implicit_order->MakeImplicit(this->last_station_visited);
02020 InsertOrder(this, implicit_order, this->cur_implicit_order_index);
02021 if (this->cur_implicit_order_index > 0) --this->cur_implicit_order_index;
02022
02023
02024
02025 uint16 &gv_flags = this->GetGroundVehicleFlags();
02026 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02027 }
02028 }
02029 }
02030 this->current_order.MakeLoading(false);
02031 }
02032
02033 if (this->last_loading_station != INVALID_STATION &&
02034 this->last_loading_station != this->last_station_visited &&
02035 ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
02036 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0)) {
02037 IncreaseStats(Station::Get(this->last_loading_station), this, this->last_station_visited);
02038 }
02039
02040 PrepareUnload(this);
02041
02042 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
02043 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02044 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
02045 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
02046
02047 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
02048 this->cur_speed = 0;
02049 this->MarkDirty();
02050 }
02051
02057 void Vehicle::CancelReservation(StationID next, Station *st)
02058 {
02059 for (Vehicle *v = this; v != NULL; v = v->next) {
02060 VehicleCargoList &cargo = v->cargo;
02061 if (cargo.ActionCount(VehicleCargoList::MTA_LOAD) > 0) {
02062 DEBUG(misc, 1, "cancelling cargo reservation");
02063 cargo.Return(UINT_MAX, &st->goods[v->cargo_type].cargo, next);
02064 cargo.SetTransferLoadPlace(st->xy);
02065 }
02066 cargo.KeepAll();
02067 }
02068 }
02069
02074 void Vehicle::LeaveStation()
02075 {
02076 assert(this->current_order.IsType(OT_LOADING));
02077
02078 delete this->cargo_payment;
02079
02080
02081 if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
02082
02083 if ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
02084 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
02085 if (this->current_order.CanLeaveWithCargo(this->last_loading_station != INVALID_STATION)) {
02086
02087
02088
02089 this->ResetRefitCaps();
02090 LinkRefresher::Run(this);
02091
02092
02093 this->last_loading_station = this->last_station_visited;
02094 } else {
02095
02096
02097 this->last_loading_station = INVALID_STATION;
02098 }
02099 }
02100
02101 this->current_order.MakeLeaveStation();
02102 Station *st = Station::Get(this->last_station_visited);
02103 this->CancelReservation(INVALID_STATION, st);
02104 st->loading_vehicles.remove(this);
02105
02106 HideFillingPercent(&this->fill_percent_te_id);
02107
02108 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
02109
02110 if (IsTileType(this->tile, MP_STATION)) {
02111 TriggerStationRandomisation(st, this->tile, SRT_TRAIN_DEPARTS);
02112 TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
02113 }
02114
02115 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
02116 }
02117
02118 this->MarkDirty();
02119 }
02120
02124 void Vehicle::ResetRefitCaps()
02125 {
02126 for (Vehicle *v = this; v != NULL; v = v->Next()) v->refit_cap = v->cargo_cap;
02127 }
02128
02134 void Vehicle::HandleLoading(bool mode)
02135 {
02136 switch (this->current_order.GetType()) {
02137 case OT_LOADING: {
02138 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
02139
02140
02141 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
02142
02143 this->PlayLeaveStationSound();
02144
02145 this->LeaveStation();
02146
02147
02148 const Order *order = this->GetOrder(this->cur_implicit_order_index);
02149 if (order == NULL ||
02150 (!order->IsType(OT_IMPLICIT) && !order->IsType(OT_GOTO_STATION)) ||
02151 order->GetDestination() != this->last_station_visited) {
02152 return;
02153 }
02154 break;
02155 }
02156
02157 case OT_DUMMY: break;
02158
02159 default: return;
02160 }
02161
02162 this->IncrementImplicitOrderIndex();
02163 }
02164
02169 void Vehicle::GetConsistFreeCapacities(SmallMap<CargoID, uint> &capacities) const
02170 {
02171 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
02172 if (v->cargo_cap == 0) continue;
02173 SmallPair<CargoID, uint> *pair = capacities.Find(v->cargo_type);
02174 if (pair == capacities.End()) {
02175 pair = capacities.Append();
02176 pair->first = v->cargo_type;
02177 pair->second = v->cargo_cap - v->cargo.StoredCount();
02178 } else {
02179 pair->second += v->cargo_cap - v->cargo.StoredCount();
02180 }
02181 }
02182 }
02183
02184 uint Vehicle::GetConsistTotalCapacity() const
02185 {
02186 uint result = 0;
02187 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
02188 result += v->cargo_cap;
02189 }
02190 return result;
02191 }
02192
02199 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
02200 {
02201 CommandCost ret = CheckOwnership(this->owner);
02202 if (ret.Failed()) return ret;
02203
02204 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
02205 if (this->IsStoppedInDepot()) return CMD_ERROR;
02206
02207 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
02208 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
02209 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
02210
02211
02212
02213 if (flags & DC_EXEC) {
02214 this->current_order.SetDepotOrderType(ODTF_MANUAL);
02215 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
02216 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02217 }
02218 return CommandCost();
02219 }
02220
02221 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR;
02222 if (flags & DC_EXEC) {
02223
02224
02225 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
02226
02227 if (this->IsGroundVehicle()) {
02228 uint16 &gv_flags = this->GetGroundVehicleFlags();
02229 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02230 }
02231
02232 this->current_order.MakeDummy();
02233 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02234 }
02235 return CommandCost();
02236 }
02237
02238 TileIndex location;
02239 DestinationID destination;
02240 bool reverse;
02241 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};
02242 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
02243
02244 if (flags & DC_EXEC) {
02245 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
02246
02247 if (this->IsGroundVehicle() && this->GetNumManualOrders() > 0) {
02248 uint16 &gv_flags = this->GetGroundVehicleFlags();
02249 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02250 }
02251
02252 this->dest_tile = location;
02253 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
02254 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
02255 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02256
02257
02258 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
02259
02260 if (this->type == VEH_AIRCRAFT) {
02261 Aircraft *a = Aircraft::From(this);
02262 if (a->state == FLYING && a->targetairport != destination) {
02263
02264 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
02265 AircraftNextAirportPos_and_Order(a);
02266 }
02267 }
02268 }
02269
02270 return CommandCost();
02271
02272 }
02273
02278 void Vehicle::UpdateVisualEffect(bool allow_power_change)
02279 {
02280 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02281 const Engine *e = this->GetEngine();
02282
02283
02284 byte visual_effect;
02285 switch (e->type) {
02286 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
02287 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
02288 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
02289 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
02290 }
02291
02292
02293 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
02294 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
02295
02296 if (callback != CALLBACK_FAILED) {
02297 if (callback >= 0x100 && e->GetGRF()->grf_version >= 8) ErrorUnknownCallbackResult(e->GetGRFID(), CBID_VEHICLE_VISUAL_EFFECT, callback);
02298
02299 callback = GB(callback, 0, 8);
02300
02301
02302 if (callback == VE_DEFAULT) {
02303 assert(HasBit(callback, VE_DISABLE_EFFECT));
02304 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
02305 }
02306 visual_effect = callback;
02307 }
02308 }
02309
02310
02311 if (visual_effect == VE_DEFAULT ||
02312 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
02313
02314
02315 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
02316 if (visual_effect == VE_DEFAULT) {
02317 visual_effect = 1 << VE_DISABLE_EFFECT;
02318 } else {
02319 SetBit(visual_effect, VE_DISABLE_EFFECT);
02320 }
02321 } else {
02322 if (visual_effect == VE_DEFAULT) {
02323
02324 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
02325 }
02326 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
02327 }
02328 }
02329
02330 this->vcache.cached_vis_effect = visual_effect;
02331
02332 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
02333 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02334 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
02335 }
02336 }
02337
02338 static const int8 _vehicle_smoke_pos[8] = {
02339 1, 1, 1, 0, -1, -1, -1, 0
02340 };
02341
02346 void Vehicle::ShowVisualEffect() const
02347 {
02348 assert(this->IsPrimaryVehicle());
02349 bool sound = false;
02350
02351
02352
02353
02354
02355
02356 if (_settings_game.vehicle.smoke_amount == 0 ||
02357 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
02358 this->cur_speed < 2) {
02359 return;
02360 }
02361
02362 uint max_speed = this->vcache.cached_max_speed;
02363 if (this->type == VEH_TRAIN) {
02364 const Train *t = Train::From(this);
02365
02366
02367
02368
02369 if (HasBit(t->flags, VRF_REVERSING) ||
02370 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
02371 t->cur_speed >= t->Train::GetCurrentMaxSpeed())) {
02372 return;
02373 }
02374
02375 max_speed = min(max_speed, t->gcache.cached_max_track_speed);
02376 max_speed = min(max_speed, this->current_order.max_speed);
02377 }
02378 if (this->type == VEH_ROAD || this->type == VEH_SHIP) max_speed = min(max_speed, this->current_order.max_speed * 2);
02379
02380 const Vehicle *v = this;
02381
02382 do {
02383 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
02384 byte effect_type = GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
02385 bool disable_effect = HasBit(v->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
02386
02387
02388
02389
02390
02391
02392
02393
02394 if (disable_effect ||
02395 v->vehstatus & VS_HIDDEN ||
02396 (MayHaveBridgeAbove(v->tile) && IsBridgeAbove(v->tile)) ||
02397 IsDepotTile(v->tile) ||
02398 IsTunnelTile(v->tile) ||
02399 (v->type == VEH_TRAIN &&
02400 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
02401 continue;
02402 }
02403
02404
02405
02406
02407 if (v->type == VEH_TRAIN) effect_offset += (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
02408
02409 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
02410 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
02411
02412 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
02413 x = -x;
02414 y = -y;
02415 }
02416
02417 switch (effect_type) {
02418 case VE_TYPE_STEAM:
02419
02420
02421
02422
02423
02424 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / max_speed))) == 0) {
02425 CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
02426 sound = true;
02427 }
02428 break;
02429
02430 case VE_TYPE_DIESEL: {
02431
02432
02433
02434
02435
02436
02437
02438
02439
02440
02441
02442 int power_weight_effect = 0;
02443 if (v->type == VEH_TRAIN) {
02444 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
02445 }
02446 if (this->cur_speed < (max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
02447 Chance16((64 - ((this->cur_speed << 5) / max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
02448 CreateEffectVehicleRel(v, x, y, 10, EV_DIESEL_SMOKE);
02449 sound = true;
02450 }
02451 break;
02452 }
02453
02454 case VE_TYPE_ELECTRIC:
02455
02456
02457
02458
02459
02460
02461 if (GB(v->tick_counter, 0, 2) == 0 &&
02462 Chance16((6 - ((this->cur_speed << 2) / max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
02463 CreateEffectVehicleRel(v, x, y, 10, EV_ELECTRIC_SPARK);
02464 sound = true;
02465 }
02466 break;
02467
02468 default:
02469 break;
02470 }
02471 } while ((v = v->Next()) != NULL);
02472
02473 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
02474 }
02475
02480 void Vehicle::SetNext(Vehicle *next)
02481 {
02482 assert(this != next);
02483
02484 if (this->next != NULL) {
02485
02486 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02487 v->first = this->next;
02488 }
02489 this->next->previous = NULL;
02490 }
02491
02492 this->next = next;
02493
02494 if (this->next != NULL) {
02495
02496 if (this->next->previous != NULL) this->next->previous->next = NULL;
02497 this->next->previous = this;
02498 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02499 v->first = this->first;
02500 }
02501 }
02502 }
02503
02509 void Vehicle::AddToShared(Vehicle *shared_chain)
02510 {
02511 assert(this->previous_shared == NULL && this->next_shared == NULL);
02512
02513 if (shared_chain->orders.list == NULL) {
02514 assert(shared_chain->previous_shared == NULL);
02515 assert(shared_chain->next_shared == NULL);
02516 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
02517 }
02518
02519 this->next_shared = shared_chain->next_shared;
02520 this->previous_shared = shared_chain;
02521
02522 shared_chain->next_shared = this;
02523
02524 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
02525
02526 shared_chain->orders.list->AddVehicle(this);
02527 }
02528
02532 void Vehicle::RemoveFromShared()
02533 {
02534
02535
02536 bool were_first = (this->FirstShared() == this);
02537 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
02538
02539 this->orders.list->RemoveVehicle(this);
02540
02541 if (!were_first) {
02542
02543 this->previous_shared->next_shared = this->NextShared();
02544 }
02545
02546 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
02547
02548
02549 if (this->orders.list->GetNumVehicles() == 1) {
02550
02551 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
02552 InvalidateVehicleOrder(this->FirstShared(), 0);
02553 } else if (were_first) {
02554
02555
02556 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
02557 }
02558
02559 this->next_shared = NULL;
02560 this->previous_shared = NULL;
02561 }
02562
02563 void VehiclesYearlyLoop()
02564 {
02565 Vehicle *v;
02566 FOR_ALL_VEHICLES(v) {
02567 if (v->IsPrimaryVehicle()) {
02568
02569 Money profit = v->GetDisplayProfitThisYear();
02570 if (v->age >= 730 && profit < 0) {
02571 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
02572 SetDParam(0, v->index);
02573 SetDParam(1, profit);
02574 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_UNPROFITABLE, v->index);
02575 }
02576 AI::NewEvent(v->owner, new ScriptEventVehicleUnprofitable(v->index));
02577 }
02578
02579 v->profit_last_year = v->profit_this_year;
02580 v->profit_this_year = 0;
02581 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
02582 }
02583 }
02584 GroupStatistics::UpdateProfits();
02585 SetWindowClassesDirty(WC_TRAINS_LIST);
02586 SetWindowClassesDirty(WC_SHIPS_LIST);
02587 SetWindowClassesDirty(WC_ROADVEH_LIST);
02588 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
02589 }
02590
02591
02601 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
02602 {
02603 const Engine *e = Engine::GetIfValid(engine_type);
02604 assert(e != NULL);
02605
02606 switch (e->type) {
02607 case VEH_TRAIN:
02608 return (st->facilities & FACIL_TRAIN) != 0;
02609
02610 case VEH_ROAD:
02611
02612
02613
02614 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
02615
02616 case VEH_SHIP:
02617 return (st->facilities & FACIL_DOCK) != 0;
02618
02619 case VEH_AIRCRAFT:
02620 return (st->facilities & FACIL_AIRPORT) != 0 &&
02621 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
02622
02623 default:
02624 return false;
02625 }
02626 }
02627
02634 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
02635 {
02636 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
02637
02638 return CanVehicleUseStation(v->engine_type, st);
02639 }
02640
02646 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
02647 {
02648 assert(this->IsGroundVehicle());
02649 if (this->type == VEH_TRAIN) {
02650 return &Train::From(this)->gcache;
02651 } else {
02652 return &RoadVehicle::From(this)->gcache;
02653 }
02654 }
02655
02661 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
02662 {
02663 assert(this->IsGroundVehicle());
02664 if (this->type == VEH_TRAIN) {
02665 return &Train::From(this)->gcache;
02666 } else {
02667 return &RoadVehicle::From(this)->gcache;
02668 }
02669 }
02670
02676 uint16 &Vehicle::GetGroundVehicleFlags()
02677 {
02678 assert(this->IsGroundVehicle());
02679 if (this->type == VEH_TRAIN) {
02680 return Train::From(this)->gv_flags;
02681 } else {
02682 return RoadVehicle::From(this)->gv_flags;
02683 }
02684 }
02685
02691 const uint16 &Vehicle::GetGroundVehicleFlags() const
02692 {
02693 assert(this->IsGroundVehicle());
02694 if (this->type == VEH_TRAIN) {
02695 return Train::From(this)->gv_flags;
02696 } else {
02697 return RoadVehicle::From(this)->gv_flags;
02698 }
02699 }
02700
02709 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
02710 {
02711 if (v->type == VEH_TRAIN) {
02712 Train *u = Train::From(v);
02713
02714 u = u->GetFirstEnginePart();
02715
02716
02717 for (; u != NULL && num_vehicles > 0; num_vehicles--) {
02718 do {
02719
02720 set.Include(u->index);
02721
02722
02723 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
02724
02725 u = u->Next();
02726 } while (u != NULL && u->IsArticulatedPart());
02727 }
02728 }
02729 }