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 "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
02042 if (this->orders.list == NULL) return;
02043
02044 uint hops = 0;
02045 const Order *first = this->orders.list->GetNextStoppingOrder(this,
02046 this->GetOrder(this->cur_implicit_order_index), hops);
02047 const Order *cur = first;
02048 const Order *next = first;
02049 while (next != NULL && cur->CanLeaveWithCargo(true)) {
02050 next = this->orders.list->GetNextStoppingOrder(this,
02051 this->orders.list->GetNext(next), ++hops);
02052 if (next == NULL) break;
02053
02054 if (next->IsType(OT_GOTO_DEPOT)) {
02055
02056 CargoID new_cid = next->GetRefitCargo();
02057 byte new_subtype = next->GetRefitSubtype();
02058 for (Vehicle *v = this; v != NULL; v = v->Next()) {
02059 const Engine *e = Engine::Get(v->engine_type);
02060 if (!HasBit(e->info.refit_mask, new_cid)) continue;
02061
02062
02063 CargoID temp_cid = v->cargo_type;
02064 byte temp_subtype = v->cargo_subtype;
02065 v->cargo_type = new_cid;
02066 v->cargo_subtype = new_subtype;
02067
02068 uint16 mail_capacity = 0;
02069 uint amount = e->DetermineCapacity(v, &mail_capacity);
02070
02071
02072 v->cargo_type = temp_cid;
02073 v->cargo_subtype = temp_subtype;
02074
02075
02076 if (new_cid != v->cargo_type && v->refit_cap > 0) {
02077 capacities[v->cargo_type] -= v->refit_cap;
02078 v->refit_cap = 0;
02079 } else if (amount < v->refit_cap) {
02080 capacities[v->cargo_type] -= v->refit_cap - amount;
02081 v->refit_cap = amount;
02082 }
02083
02084
02085 if (v->type == VEH_AIRCRAFT) {
02086 Vehicle *u = v->Next();
02087 if (mail_capacity < u->refit_cap) {
02088 capacities[u->cargo_type] -= u->refit_cap - mail_capacity;
02089 u->refit_cap = mail_capacity;
02090 }
02091 break;
02092 }
02093 if (v->type == VEH_SHIP) break;
02094 }
02095 } else {
02096 StationID next_station = next->GetDestination();
02097 Station *st = Station::GetIfValid(cur->GetDestination());
02098 if (st != NULL && next_station != INVALID_STATION && next_station != st->index) {
02099 for (const SmallPair<CargoID, uint> *i = capacities.Begin(); i != capacities.End(); ++i) {
02100
02101 if (i->second > 0) IncreaseStats(st, i->first, next_station, i->second, UINT_MAX);
02102 }
02103 }
02104 cur = next;
02105 if (cur == first) break;
02106 }
02107 }
02108
02109 for (Vehicle *v = this; v != NULL; v = v->Next()) v->refit_cap = v->cargo_cap;
02110 }
02111
02117 void Vehicle::HandleLoading(bool mode)
02118 {
02119 switch (this->current_order.GetType()) {
02120 case OT_LOADING: {
02121 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
02122
02123
02124 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
02125
02126 this->PlayLeaveStationSound();
02127
02128 this->LeaveStation();
02129
02130
02131 const Order *order = this->GetOrder(this->cur_implicit_order_index);
02132 if (order == NULL ||
02133 (!order->IsType(OT_IMPLICIT) && !order->IsType(OT_GOTO_STATION)) ||
02134 order->GetDestination() != this->last_station_visited) {
02135 return;
02136 }
02137 break;
02138 }
02139
02140 case OT_DUMMY: break;
02141
02142 default: return;
02143 }
02144
02145 this->IncrementImplicitOrderIndex();
02146 }
02147
02152 void Vehicle::GetConsistFreeCapacities(SmallMap<CargoID, uint> &capacities) const
02153 {
02154 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
02155 if (v->cargo_cap == 0) continue;
02156 SmallPair<CargoID, uint> *pair = capacities.Find(v->cargo_type);
02157 if (pair == capacities.End()) {
02158 pair = capacities.Append();
02159 pair->first = v->cargo_type;
02160 pair->second = v->cargo_cap - v->cargo.Count();
02161 } else {
02162 pair->second += v->cargo_cap - v->cargo.Count();
02163 }
02164 }
02165 }
02166
02167 uint Vehicle::GetConsistTotalCapacity() const
02168 {
02169 uint result = 0;
02170 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
02171 result += v->cargo_cap;
02172 }
02173 return result;
02174 }
02175
02182 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
02183 {
02184 CommandCost ret = CheckOwnership(this->owner);
02185 if (ret.Failed()) return ret;
02186
02187 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
02188 if (this->IsStoppedInDepot()) return CMD_ERROR;
02189
02190 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
02191 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
02192 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
02193
02194
02195
02196 if (flags & DC_EXEC) {
02197 this->current_order.SetDepotOrderType(ODTF_MANUAL);
02198 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
02199 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
02200 }
02201 return CommandCost();
02202 }
02203
02204 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR;
02205 if (flags & DC_EXEC) {
02206
02207
02208 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
02209
02210 if (this->IsGroundVehicle()) {
02211 uint16 &gv_flags = this->GetGroundVehicleFlags();
02212 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02213 }
02214
02215 this->current_order.MakeDummy();
02216 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
02217 }
02218 return CommandCost();
02219 }
02220
02221 TileIndex location;
02222 DestinationID destination;
02223 bool reverse;
02224 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};
02225 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
02226
02227 if (flags & DC_EXEC) {
02228 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
02229
02230 if (this->IsGroundVehicle()) {
02231 uint16 &gv_flags = this->GetGroundVehicleFlags();
02232 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02233 }
02234
02235 this->dest_tile = location;
02236 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
02237 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
02238 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
02239
02240
02241 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
02242
02243 if (this->type == VEH_AIRCRAFT) {
02244 Aircraft *a = Aircraft::From(this);
02245 if (a->state == FLYING && a->targetairport != destination) {
02246
02247 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
02248 AircraftNextAirportPos_and_Order(a);
02249 }
02250 }
02251 }
02252
02253 return CommandCost();
02254
02255 }
02256
02261 void Vehicle::UpdateVisualEffect(bool allow_power_change)
02262 {
02263 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02264 const Engine *e = this->GetEngine();
02265
02266
02267 byte visual_effect;
02268 switch (e->type) {
02269 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
02270 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
02271 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
02272 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
02273 }
02274
02275
02276 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
02277 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
02278
02279 if (callback != CALLBACK_FAILED) {
02280 if (callback >= 0x100 && e->GetGRF()->grf_version >= 8) ErrorUnknownCallbackResult(e->GetGRFID(), CBID_VEHICLE_VISUAL_EFFECT, callback);
02281
02282 callback = GB(callback, 0, 8);
02283
02284
02285 if (callback == VE_DEFAULT) {
02286 assert(HasBit(callback, VE_DISABLE_EFFECT));
02287 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
02288 }
02289 visual_effect = callback;
02290 }
02291 }
02292
02293
02294 if (visual_effect == VE_DEFAULT ||
02295 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
02296
02297
02298 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
02299 if (visual_effect == VE_DEFAULT) {
02300 visual_effect = 1 << VE_DISABLE_EFFECT;
02301 } else {
02302 SetBit(visual_effect, VE_DISABLE_EFFECT);
02303 }
02304 } else {
02305 if (visual_effect == VE_DEFAULT) {
02306
02307 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
02308 }
02309 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
02310 }
02311 }
02312
02313 this->vcache.cached_vis_effect = visual_effect;
02314
02315 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
02316 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02317 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
02318 }
02319 }
02320
02321 static const int8 _vehicle_smoke_pos[8] = {
02322 1, 1, 1, 0, -1, -1, -1, 0
02323 };
02324
02329 void Vehicle::ShowVisualEffect() const
02330 {
02331 assert(this->IsPrimaryVehicle());
02332 bool sound = false;
02333
02334
02335
02336
02337
02338
02339 if (_settings_game.vehicle.smoke_amount == 0 ||
02340 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
02341 this->cur_speed < 2) {
02342 return;
02343 }
02344 if (this->type == VEH_TRAIN) {
02345 const Train *t = Train::From(this);
02346
02347
02348
02349
02350 if (HasBit(t->flags, VRF_REVERSING) ||
02351 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
02352 t->cur_speed >= t->Train::GetCurrentMaxSpeed())) {
02353 return;
02354 }
02355 }
02356
02357 const Vehicle *v = this;
02358
02359 do {
02360 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
02361 byte effect_type = GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
02362 bool disable_effect = HasBit(v->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
02363
02364
02365
02366
02367
02368
02369
02370
02371 if (disable_effect ||
02372 v->vehstatus & VS_HIDDEN ||
02373 (MayHaveBridgeAbove(v->tile) && IsBridgeAbove(v->tile)) ||
02374 IsDepotTile(v->tile) ||
02375 IsTunnelTile(v->tile) ||
02376 (v->type == VEH_TRAIN &&
02377 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
02378 continue;
02379 }
02380
02381
02382
02383
02384 if (v->type == VEH_TRAIN) effect_offset += (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
02385
02386 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
02387 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
02388
02389 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
02390 x = -x;
02391 y = -y;
02392 }
02393
02394 switch (effect_type) {
02395 case VE_TYPE_STEAM:
02396
02397
02398
02399
02400
02401 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / this->vcache.cached_max_speed))) == 0) {
02402 CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
02403 sound = true;
02404 }
02405 break;
02406
02407 case VE_TYPE_DIESEL: {
02408
02409
02410
02411
02412
02413
02414
02415
02416
02417
02418
02419 int power_weight_effect = 0;
02420 if (v->type == VEH_TRAIN) {
02421 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
02422 }
02423 if (this->cur_speed < (this->vcache.cached_max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
02424 Chance16((64 - ((this->cur_speed << 5) / this->vcache.cached_max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
02425 CreateEffectVehicleRel(v, x, y, 10, EV_DIESEL_SMOKE);
02426 sound = true;
02427 }
02428 break;
02429 }
02430
02431 case VE_TYPE_ELECTRIC:
02432
02433
02434
02435
02436
02437
02438 if (GB(v->tick_counter, 0, 2) == 0 &&
02439 Chance16((6 - ((this->cur_speed << 2) / this->vcache.cached_max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
02440 CreateEffectVehicleRel(v, x, y, 10, EV_ELECTRIC_SPARK);
02441 sound = true;
02442 }
02443 break;
02444
02445 default:
02446 break;
02447 }
02448 } while ((v = v->Next()) != NULL);
02449
02450 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
02451 }
02452
02457 void Vehicle::SetNext(Vehicle *next)
02458 {
02459 assert(this != next);
02460
02461 if (this->next != NULL) {
02462
02463 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02464 v->first = this->next;
02465 }
02466 this->next->previous = NULL;
02467 }
02468
02469 this->next = next;
02470
02471 if (this->next != NULL) {
02472
02473 if (this->next->previous != NULL) this->next->previous->next = NULL;
02474 this->next->previous = this;
02475 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02476 v->first = this->first;
02477 }
02478 }
02479 }
02480
02486 void Vehicle::AddToShared(Vehicle *shared_chain)
02487 {
02488 assert(this->previous_shared == NULL && this->next_shared == NULL);
02489
02490 if (shared_chain->orders.list == NULL) {
02491 assert(shared_chain->previous_shared == NULL);
02492 assert(shared_chain->next_shared == NULL);
02493 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
02494 }
02495
02496 this->next_shared = shared_chain->next_shared;
02497 this->previous_shared = shared_chain;
02498
02499 shared_chain->next_shared = this;
02500
02501 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
02502
02503 shared_chain->orders.list->AddVehicle(this);
02504 }
02505
02509 void Vehicle::RemoveFromShared()
02510 {
02511
02512
02513 bool were_first = (this->FirstShared() == this);
02514 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
02515
02516 this->orders.list->RemoveVehicle(this);
02517
02518 if (!were_first) {
02519
02520 this->previous_shared->next_shared = this->NextShared();
02521 }
02522
02523 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
02524
02525
02526 if (this->orders.list->GetNumVehicles() == 1) {
02527
02528 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
02529 InvalidateVehicleOrder(this->FirstShared(), 0);
02530 } else if (were_first) {
02531
02532
02533 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
02534 }
02535
02536 this->next_shared = NULL;
02537 this->previous_shared = NULL;
02538 }
02539
02540 void VehiclesYearlyLoop()
02541 {
02542 Vehicle *v;
02543 FOR_ALL_VEHICLES(v) {
02544 if (v->IsPrimaryVehicle()) {
02545
02546 Money profit = v->GetDisplayProfitThisYear();
02547 if (v->age >= 730 && profit < 0) {
02548 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
02549 SetDParam(0, v->index);
02550 SetDParam(1, profit);
02551 AddVehicleNewsItem(
02552 STR_NEWS_VEHICLE_IS_UNPROFITABLE,
02553 NS_ADVICE,
02554 v->index
02555 );
02556 }
02557 AI::NewEvent(v->owner, new ScriptEventVehicleUnprofitable(v->index));
02558 }
02559
02560 v->profit_last_year = v->profit_this_year;
02561 v->profit_this_year = 0;
02562 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
02563 }
02564 }
02565 GroupStatistics::UpdateProfits();
02566 SetWindowClassesDirty(WC_TRAINS_LIST);
02567 SetWindowClassesDirty(WC_SHIPS_LIST);
02568 SetWindowClassesDirty(WC_ROADVEH_LIST);
02569 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
02570 }
02571
02572
02582 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
02583 {
02584 const Engine *e = Engine::GetIfValid(engine_type);
02585 assert(e != NULL);
02586
02587 switch (e->type) {
02588 case VEH_TRAIN:
02589 return (st->facilities & FACIL_TRAIN) != 0;
02590
02591 case VEH_ROAD:
02592
02593
02594
02595 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
02596
02597 case VEH_SHIP:
02598 return (st->facilities & FACIL_DOCK) != 0;
02599
02600 case VEH_AIRCRAFT:
02601 return (st->facilities & FACIL_AIRPORT) != 0 &&
02602 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
02603
02604 default:
02605 return false;
02606 }
02607 }
02608
02615 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
02616 {
02617 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
02618
02619 return CanVehicleUseStation(v->engine_type, st);
02620 }
02621
02627 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
02628 {
02629 assert(this->IsGroundVehicle());
02630 if (this->type == VEH_TRAIN) {
02631 return &Train::From(this)->gcache;
02632 } else {
02633 return &RoadVehicle::From(this)->gcache;
02634 }
02635 }
02636
02642 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
02643 {
02644 assert(this->IsGroundVehicle());
02645 if (this->type == VEH_TRAIN) {
02646 return &Train::From(this)->gcache;
02647 } else {
02648 return &RoadVehicle::From(this)->gcache;
02649 }
02650 }
02651
02657 uint16 &Vehicle::GetGroundVehicleFlags()
02658 {
02659 assert(this->IsGroundVehicle());
02660 if (this->type == VEH_TRAIN) {
02661 return Train::From(this)->gv_flags;
02662 } else {
02663 return RoadVehicle::From(this)->gv_flags;
02664 }
02665 }
02666
02672 const uint16 &Vehicle::GetGroundVehicleFlags() const
02673 {
02674 assert(this->IsGroundVehicle());
02675 if (this->type == VEH_TRAIN) {
02676 return Train::From(this)->gv_flags;
02677 } else {
02678 return RoadVehicle::From(this)->gv_flags;
02679 }
02680 }
02681
02690 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
02691 {
02692 if (v->type == VEH_TRAIN) {
02693 Train *u = Train::From(v);
02694
02695 u = u->GetFirstEnginePart();
02696
02697
02698 for (; u != NULL && num_vehicles > 0; num_vehicles--) {
02699 do {
02700
02701 set.Include(u->index);
02702
02703
02704 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
02705
02706 u = u->Next();
02707 } while (u != NULL && u->IsArticulatedPart());
02708 }
02709 }
02710 }