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