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