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