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
00904 bool shadowed = (v->vehstatus & VS_SHADOW);
00905
00906 if (v->type == VEH_EFFECT) {
00907
00908
00909 TransparencyOption to = EffectVehicle::From(v)->GetTransparencyOption();
00910 if (to != TO_INVALID && (IsTransparencySet(to) || IsInvisibilitySet(to))) return;
00911 }
00912
00913 AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
00914 v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed);
00915 }
00916
00921 void ViewportAddVehicles(DrawPixelInfo *dpi)
00922 {
00923
00924 const int l = dpi->left;
00925 const int r = dpi->left + dpi->width;
00926 const int t = dpi->top;
00927 const int b = dpi->top + dpi->height;
00928
00929
00930 int xl, xu, yl, yu;
00931
00932 if (dpi->width + 70 < (1 << (7 + 6))) {
00933 xl = GB(l - 70, 7, 6);
00934 xu = GB(r, 7, 6);
00935 } else {
00936
00937 xl = 0;
00938 xu = 0x3F;
00939 }
00940
00941 if (dpi->height + 70 < (1 << (6 + 6))) {
00942 yl = GB(t - 70, 6, 6) << 6;
00943 yu = GB(b, 6, 6) << 6;
00944 } else {
00945
00946 yl = 0;
00947 yu = 0x3F << 6;
00948 }
00949
00950 for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
00951 for (int x = xl;; x = (x + 1) & 0x3F) {
00952 const Vehicle *v = _vehicle_position_hash[x + y];
00953
00954 while (v != NULL) {
00955 if (!(v->vehstatus & VS_HIDDEN) &&
00956 l <= v->coord.right &&
00957 t <= v->coord.bottom &&
00958 r >= v->coord.left &&
00959 b >= v->coord.top) {
00960 DoDrawVehicle(v);
00961 }
00962 v = v->next_hash;
00963 }
00964
00965 if (x == xu) break;
00966 }
00967
00968 if (y == yu) break;
00969 }
00970 }
00971
00979 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
00980 {
00981 Vehicle *found = NULL, *v;
00982 uint dist, best_dist = UINT_MAX;
00983
00984 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
00985
00986 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
00987 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
00988
00989 FOR_ALL_VEHICLES(v) {
00990 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
00991 x >= v->coord.left && x <= v->coord.right &&
00992 y >= v->coord.top && y <= v->coord.bottom) {
00993
00994 dist = max(
00995 abs(((v->coord.left + v->coord.right) >> 1) - x),
00996 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
00997 );
00998
00999 if (dist < best_dist) {
01000 found = v;
01001 best_dist = dist;
01002 }
01003 }
01004 }
01005
01006 return found;
01007 }
01008
01013 void DecreaseVehicleValue(Vehicle *v)
01014 {
01015 v->value -= v->value >> 8;
01016 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01017 }
01018
01019 static const byte _breakdown_chance[64] = {
01020 3, 3, 3, 3, 3, 3, 3, 3,
01021 4, 4, 5, 5, 6, 6, 7, 7,
01022 8, 8, 9, 9, 10, 10, 11, 11,
01023 12, 13, 13, 13, 13, 14, 15, 16,
01024 17, 19, 21, 25, 28, 31, 34, 37,
01025 40, 44, 48, 52, 56, 60, 64, 68,
01026 72, 80, 90, 100, 110, 120, 130, 140,
01027 150, 170, 190, 210, 230, 250, 250, 250,
01028 };
01029
01030 void CheckVehicleBreakdown(Vehicle *v)
01031 {
01032 int rel, rel_old;
01033
01034
01035 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
01036 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01037
01038 if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
01039 _settings_game.difficulty.vehicle_breakdowns < 1 ||
01040 v->cur_speed < 5 || _game_mode == GM_MENU) {
01041 return;
01042 }
01043
01044 uint32 r = Random();
01045
01046
01047 int chance = v->breakdown_chance + 1;
01048 if (Chance16I(1, 25, r)) chance += 25;
01049 v->breakdown_chance = min(255, chance);
01050
01051
01052 rel = v->reliability;
01053 if (v->type == VEH_SHIP) rel += 0x6666;
01054
01055
01056 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
01057
01058
01059 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
01060 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
01061 v->breakdown_delay = GB(r, 24, 7) + 0x80;
01062 v->breakdown_chance = 0;
01063 }
01064 }
01065
01072 bool Vehicle::HandleBreakdown()
01073 {
01074
01075
01076
01077
01078
01079 switch (this->breakdown_ctr) {
01080 case 0:
01081 return false;
01082
01083 case 2:
01084 this->breakdown_ctr = 1;
01085
01086 if (this->breakdowns_since_last_service != 255) {
01087 this->breakdowns_since_last_service++;
01088 }
01089
01090 this->MarkDirty();
01091 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01092 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01093
01094 if (this->type == VEH_AIRCRAFT) {
01095
01096 this->vehstatus |= VS_AIRCRAFT_BROKEN;
01097 } else {
01098 this->cur_speed = 0;
01099
01100 if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
01101 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
01102 (this->type == VEH_TRAIN ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
01103 (this->type == VEH_TRAIN ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
01104 }
01105
01106 if (!(this->vehstatus & VS_HIDDEN)) {
01107 EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
01108 if (u != NULL) u->animation_state = this->breakdown_delay * 2;
01109 }
01110 }
01111
01112 case 1:
01113
01114 if (this->type == VEH_AIRCRAFT) return false;
01115
01116
01117 if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
01118 if (--this->breakdown_delay == 0) {
01119 this->breakdown_ctr = 0;
01120 this->MarkDirty();
01121 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01122 }
01123 }
01124 return true;
01125
01126 default:
01127 if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
01128 return false;
01129 }
01130 }
01131
01136 void AgeVehicle(Vehicle *v)
01137 {
01138 if (v->age < MAX_DAY) v->age++;
01139
01140 int age = v->age - v->max_age;
01141 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
01142 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
01143 v->reliability_spd_dec <<= 1;
01144 }
01145
01146 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01147
01148
01149 if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
01150
01151
01152 if (Company::Get(v->owner)->settings.engine_renew && Engine::Get(v->engine_type)->company_avail != 0) return;
01153
01154 StringID str;
01155 if (age == -DAYS_IN_LEAP_YEAR) {
01156 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
01157 } else if (age == 0) {
01158 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
01159 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
01160 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
01161 } else {
01162 return;
01163 }
01164
01165 SetDParam(0, v->index);
01166 AddVehicleNewsItem(str, NS_ADVICE, v->index);
01167 }
01168
01175 uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *colour)
01176 {
01177 int count = 0;
01178 int max = 0;
01179 int cars = 0;
01180 int unloading = 0;
01181 bool loading = false;
01182
01183 const Vehicle *u = v;
01184
01185 const Station *st = Station::GetIfValid(v->last_station_visited);
01186 assert(colour == NULL || st != NULL);
01187
01188
01189 for (; v != NULL; v = v->Next()) {
01190 count += v->cargo.OnboardCount();
01191 max += v->cargo_cap;
01192 if (v->cargo_cap != 0 && colour != NULL) {
01193 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
01194 loading |= !(u->current_order.GetLoadType() & OLFB_NO_LOAD) && st->goods[v->cargo_type].days_since_pickup != 255;
01195 cars++;
01196 }
01197 }
01198
01199 if (colour != NULL) {
01200 if (unloading == 0 && loading) {
01201 *colour = STR_PERCENT_UP;
01202 } else if (cars == unloading || !loading) {
01203 *colour = STR_PERCENT_DOWN;
01204 } else {
01205 *colour = STR_PERCENT_UP_DOWN;
01206 }
01207 }
01208
01209
01210 if (max == 0) return 100;
01211
01212
01213 return (count * 100) / max;
01214 }
01215
01220 void VehicleEnterDepot(Vehicle *v)
01221 {
01222
01223 assert(v == v->First());
01224
01225 switch (v->type) {
01226 case VEH_TRAIN: {
01227 Train *t = Train::From(v);
01228 SetWindowClassesDirty(WC_TRAINS_LIST);
01229
01230 SetDepotReservation(t->tile, false);
01231 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
01232
01233 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
01234 t->wait_counter = 0;
01235 t->force_proceed = TFP_NONE;
01236 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
01237 t->ConsistChanged(true);
01238 break;
01239 }
01240
01241 case VEH_ROAD:
01242 SetWindowClassesDirty(WC_ROADVEH_LIST);
01243 break;
01244
01245 case VEH_SHIP: {
01246 SetWindowClassesDirty(WC_SHIPS_LIST);
01247 Ship *ship = Ship::From(v);
01248 ship->state = TRACK_BIT_DEPOT;
01249 ship->UpdateCache();
01250 ship->UpdateViewport(true, true);
01251 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01252 break;
01253 }
01254
01255 case VEH_AIRCRAFT:
01256 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01257 HandleAircraftEnterHangar(Aircraft::From(v));
01258 break;
01259 default: NOT_REACHED();
01260 }
01261 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01262
01263 if (v->type != VEH_TRAIN) {
01264
01265
01266 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01267 }
01268 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01269
01270 v->vehstatus |= VS_HIDDEN;
01271 v->cur_speed = 0;
01272
01273 VehicleServiceInDepot(v);
01274
01275 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01276
01277 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01278 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01279
01280 const Order *real_order = v->GetOrder(v->cur_real_order_index);
01281 Order t = v->current_order;
01282 v->current_order.MakeDummy();
01283
01284
01285
01286 if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
01287 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
01288 (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
01289
01290 return;
01291 }
01292
01293 if (t.IsRefit()) {
01294 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01295 CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
01296 cur_company.Restore();
01297
01298 if (cost.Failed()) {
01299 _vehicles_to_autoreplace[v] = false;
01300 if (v->owner == _local_company) {
01301
01302 SetDParam(0, v->index);
01303 AddVehicleNewsItem(STR_NEWS_ORDER_REFIT_FAILED, NS_ADVICE, v->index);
01304 }
01305 } else if (cost.GetCost() != 0) {
01306 v->profit_this_year -= cost.GetCost() << 8;
01307 if (v->owner == _local_company) {
01308 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01309 }
01310 }
01311 }
01312
01313 if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01314
01315 v->DeleteUnreachedImplicitOrders();
01316 UpdateVehicleTimetable(v, true);
01317 v->IncrementImplicitOrderIndex();
01318 }
01319 if (t.GetDepotActionType() & ODATFB_HALT) {
01320
01321 _vehicles_to_autoreplace[v] = false;
01322 if (v->owner == _local_company) {
01323 SetDParam(0, v->index);
01324 AddVehicleNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, NS_ADVICE, v->index);
01325 }
01326 AI::NewEvent(v->owner, new AIEventVehicleWaitingInDepot(v->index));
01327 }
01328 }
01329 }
01330
01331
01339 void VehicleMove(Vehicle *v, bool update_viewport)
01340 {
01341 int img = v->cur_image;
01342 Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
01343 const Sprite *spr = GetSprite(img, ST_NORMAL);
01344
01345 pt.x += spr->x_offs;
01346 pt.y += spr->y_offs;
01347
01348 UpdateVehiclePosHash(v, pt.x, pt.y);
01349
01350 Rect old_coord = v->coord;
01351 v->coord.left = pt.x;
01352 v->coord.top = pt.y;
01353 v->coord.right = pt.x + spr->width + 2;
01354 v->coord.bottom = pt.y + spr->height + 2;
01355
01356 if (update_viewport) {
01357 MarkAllViewportsDirty(
01358 min(old_coord.left, v->coord.left),
01359 min(old_coord.top, v->coord.top),
01360 max(old_coord.right, v->coord.right) + 1,
01361 max(old_coord.bottom, v->coord.bottom) + 1
01362 );
01363 }
01364 }
01365
01374 void MarkSingleVehicleDirty(const Vehicle *v)
01375 {
01376 MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1, v->coord.bottom + 1);
01377 }
01378
01384 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01385 {
01386 static const int8 _delta_coord[16] = {
01387 -1,-1,-1, 0, 1, 1, 1, 0,
01388 -1, 0, 1, 1, 1, 0,-1,-1,
01389 };
01390
01391 int x = v->x_pos + _delta_coord[v->direction];
01392 int y = v->y_pos + _delta_coord[v->direction + 8];
01393
01394 GetNewVehiclePosResult gp;
01395 gp.x = x;
01396 gp.y = y;
01397 gp.old_tile = v->tile;
01398 gp.new_tile = TileVirtXY(x, y);
01399 return gp;
01400 }
01401
01402 static const Direction _new_direction_table[] = {
01403 DIR_N, DIR_NW, DIR_W,
01404 DIR_NE, DIR_SE, DIR_SW,
01405 DIR_E, DIR_SE, DIR_S
01406 };
01407
01408 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01409 {
01410 int i = 0;
01411
01412 if (y >= v->y_pos) {
01413 if (y != v->y_pos) i += 3;
01414 i += 3;
01415 }
01416
01417 if (x >= v->x_pos) {
01418 if (x != v->x_pos) i++;
01419 i++;
01420 }
01421
01422 Direction dir = v->direction;
01423
01424 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01425 if (dirdiff == DIRDIFF_SAME) return dir;
01426 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01427 }
01428
01438 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01439 {
01440 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01441 }
01442
01450 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01451 {
01452
01453 const Vehicle *v;
01454 FOR_ALL_VEHICLES(v) {
01455 if (v->type == type && v->owner == owner) {
01456 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01457 }
01458 }
01459
01460 if (this->maxid == 0) return;
01461
01462
01463
01464
01465 this->cache = CallocT<bool>(this->maxid + 2);
01466
01467
01468 FOR_ALL_VEHICLES(v) {
01469 if (v->type == type && v->owner == owner) {
01470 this->cache[v->unitnumber] = true;
01471 }
01472 }
01473 }
01474
01476 UnitID FreeUnitIDGenerator::NextID()
01477 {
01478 if (this->maxid <= this->curid) return ++this->curid;
01479
01480 while (this->cache[++this->curid]) { }
01481
01482 return this->curid;
01483 }
01484
01490 UnitID GetFreeUnitNumber(VehicleType type)
01491 {
01492
01493 uint max_veh;
01494 switch (type) {
01495 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
01496 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
01497 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
01498 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
01499 default: NOT_REACHED();
01500 }
01501
01502 uint amounts[4];
01503 CountCompanyVehicles(_current_company, amounts);
01504 assert((uint)type < lengthof(amounts));
01505 if (amounts[type] >= max_veh) return UINT16_MAX;
01506
01507 FreeUnitIDGenerator gen(type, _current_company);
01508
01509 return gen.NextID();
01510 }
01511
01512
01521 bool CanBuildVehicleInfrastructure(VehicleType type)
01522 {
01523 assert(IsCompanyBuildableVehicleType(type));
01524
01525 if (!Company::IsValidID(_local_company)) return false;
01526 if (!_settings_client.gui.disable_unsuitable_building) return true;
01527
01528 UnitID max;
01529 switch (type) {
01530 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
01531 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
01532 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
01533 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01534 default: NOT_REACHED();
01535 }
01536
01537
01538 if (max > 0) {
01539
01540 const Engine *e;
01541 FOR_ALL_ENGINES_OF_TYPE(e, type) {
01542 if (HasBit(e->company_avail, _local_company)) return true;
01543 }
01544 return false;
01545 }
01546
01547
01548 const Vehicle *v;
01549 FOR_ALL_VEHICLES(v) {
01550 if (v->owner == _local_company && v->type == type) return true;
01551 }
01552
01553 return false;
01554 }
01555
01556
01564 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
01565 {
01566 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01567 const Engine *e = Engine::Get(engine_type);
01568 switch (e->type) {
01569 default: NOT_REACHED();
01570 case VEH_TRAIN:
01571 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
01572
01573
01574 engine_type = parent_engine_type;
01575 e = Engine::Get(engine_type);
01576
01577 }
01578
01579 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01580 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01581 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
01582 if (!CargoSpec::Get(cargo_type)->is_freight) {
01583 if (parent_engine_type == INVALID_ENGINE) {
01584 return LS_PASSENGER_WAGON_STEAM;
01585 } else {
01586 switch (RailVehInfo(parent_engine_type)->engclass) {
01587 default: NOT_REACHED();
01588 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
01589 case EC_DIESEL: return LS_PASSENGER_WAGON_DIESEL;
01590 case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
01591 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
01592 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
01593 }
01594 }
01595 } else {
01596 return LS_FREIGHT_WAGON;
01597 }
01598 } else {
01599 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
01600
01601 switch (e->u.rail.engclass) {
01602 default: NOT_REACHED();
01603 case EC_STEAM: return LS_STEAM;
01604 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
01605 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
01606 case EC_MONORAIL: return LS_MONORAIL;
01607 case EC_MAGLEV: return LS_MAGLEV;
01608 }
01609 }
01610
01611 case VEH_ROAD:
01612
01613 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01614 engine_type = parent_engine_type;
01615 e = Engine::Get(engine_type);
01616 cargo_type = v->First()->cargo_type;
01617 }
01618 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01619 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01620
01621
01622 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
01623
01624 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01625 } else {
01626
01627 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01628 }
01629
01630 case VEH_SHIP:
01631 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01632 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01633 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01634
01635 case VEH_AIRCRAFT:
01636 switch (e->u.air.subtype) {
01637 case AIR_HELI: return LS_HELICOPTER;
01638 case AIR_CTOL: return LS_SMALL_PLANE;
01639 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
01640 default: NOT_REACHED();
01641 }
01642 }
01643 }
01644
01654 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
01655 {
01656 const Company *c = Company::Get(company);
01657 LiveryScheme scheme = LS_DEFAULT;
01658
01659
01660
01661 if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
01662
01663 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
01664
01665
01666 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01667 }
01668
01669 return &c->livery[scheme];
01670 }
01671
01672
01673 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01674 {
01675 PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01676
01677
01678 if (map != PAL_NONE) return map;
01679
01680 const Engine *e = Engine::Get(engine_type);
01681
01682
01683 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
01684 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01685
01686 if (callback != CALLBACK_FAILED) {
01687 assert_compile(PAL_NONE == 0);
01688 map = GB(callback, 0, 14);
01689
01690
01691 if (!HasBit(callback, 14)) {
01692
01693 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01694 return map;
01695 }
01696 }
01697 }
01698
01699 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
01700
01701 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
01702
01703
01704 if (!Company::IsValidID(company)) return map;
01705
01706 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
01707
01708 map += livery->colour1;
01709 if (twocc) map += livery->colour2 * 16;
01710
01711
01712 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01713 return map;
01714 }
01715
01722 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
01723 {
01724 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01725 }
01726
01732 PaletteID GetVehiclePalette(const Vehicle *v)
01733 {
01734 if (v->IsGroundVehicle()) {
01735 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
01736 }
01737
01738 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01739 }
01740
01749 uint GetVehicleCapacity(const Vehicle *v, uint16 *mail_capacity)
01750 {
01751 if (mail_capacity != NULL) *mail_capacity = 0;
01752 const Engine *e = Engine::Get(v->engine_type);
01753
01754 if (!e->CanCarryCargo()) return 0;
01755
01756 if (mail_capacity != NULL && e->type == VEH_AIRCRAFT && IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
01757 *mail_capacity = GetVehicleProperty(v, PROP_AIRCRAFT_MAIL_CAPACITY, e->u.air.mail_capacity);
01758 }
01759 CargoID default_cargo = e->GetDefaultCargoType();
01760
01761
01762
01763 if (HasBit(e->info.callback_mask, CBM_VEHICLE_REFIT_CAPACITY) &&
01764 (default_cargo != v->cargo_type || v->cargo_subtype != 0)) {
01765 uint16 callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
01766 if (callback != CALLBACK_FAILED) return callback;
01767 }
01768
01769
01770 uint capacity;
01771 switch (e->type) {
01772 case VEH_TRAIN: capacity = GetVehicleProperty(v, PROP_TRAIN_CARGO_CAPACITY, e->u.rail.capacity); break;
01773 case VEH_ROAD: capacity = GetVehicleProperty(v, PROP_ROADVEH_CARGO_CAPACITY, e->u.road.capacity); break;
01774 case VEH_SHIP: capacity = GetVehicleProperty(v, PROP_SHIP_CARGO_CAPACITY, e->u.ship.capacity); break;
01775 case VEH_AIRCRAFT: capacity = GetVehicleProperty(v, PROP_AIRCRAFT_PASSENGER_CAPACITY, e->u.air.passenger_capacity); break;
01776 default: NOT_REACHED();
01777 }
01778
01779
01780
01781 if (e->type != VEH_SHIP) {
01782 if (e->type == VEH_AIRCRAFT) {
01783 if (!IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
01784 capacity += GetVehicleProperty(v, PROP_AIRCRAFT_MAIL_CAPACITY, e->u.air.mail_capacity);
01785 }
01786 if (v->cargo_type == CT_MAIL) return capacity;
01787 } else {
01788 switch (default_cargo) {
01789 case CT_PASSENGERS: break;
01790 case CT_MAIL:
01791 case CT_GOODS: capacity *= 2; break;
01792 default: capacity *= 4; break;
01793 }
01794 }
01795 switch (v->cargo_type) {
01796 case CT_PASSENGERS: break;
01797 case CT_MAIL:
01798 case CT_GOODS: capacity /= 2; break;
01799 default: capacity /= 4; break;
01800 }
01801 }
01802
01803 return capacity;
01804 }
01805
01809 void Vehicle::DeleteUnreachedImplicitOrders()
01810 {
01811 if (this->IsGroundVehicle()) {
01812 uint16 &gv_flags = this->GetGroundVehicleFlags();
01813 if (HasBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)) {
01814
01815 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
01816 this->cur_implicit_order_index = this->cur_real_order_index;
01817 InvalidateVehicleOrder(this, 0);
01818 return;
01819 }
01820 }
01821
01822 const Order *order = this->GetOrder(this->cur_implicit_order_index);
01823 while (order != NULL) {
01824 if (this->cur_implicit_order_index == this->cur_real_order_index) break;
01825
01826 if (order->IsType(OT_IMPLICIT)) {
01827
01828 order = order->next;
01829 DeleteOrder(this, this->cur_implicit_order_index);
01830 } else {
01831
01832 order = order->next;
01833 this->cur_implicit_order_index++;
01834 }
01835
01836
01837 if (order == NULL) {
01838 order = this->GetOrder(0);
01839 this->cur_implicit_order_index = 0;
01840 }
01841 }
01842 }
01843
01848 void Vehicle::BeginLoading()
01849 {
01850 assert(IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP);
01851
01852 if (this->current_order.IsType(OT_GOTO_STATION) &&
01853 this->current_order.GetDestination() == this->last_station_visited) {
01854 this->DeleteUnreachedImplicitOrders();
01855
01856
01857 this->current_order.MakeLoading(true);
01858 UpdateVehicleTimetable(this, true);
01859
01860
01861
01862
01863
01864
01865 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
01866
01867 } else {
01868
01869
01870
01871
01872
01873 Order *in_list = this->GetOrder(this->cur_implicit_order_index);
01874 if (this->IsGroundVehicle() && in_list != NULL &&
01875 (!in_list->IsType(OT_IMPLICIT) ||
01876 in_list->GetDestination() != this->last_station_visited)) {
01877 bool suppress_implicit_orders = HasBit(this->GetGroundVehicleFlags(), GVF_SUPPRESS_IMPLICIT_ORDERS);
01878
01879 Order *prev_order = this->cur_implicit_order_index > 0 ? this->GetOrder(this->cur_implicit_order_index - 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : NULL);
01880 if (prev_order == NULL ||
01881 (!prev_order->IsType(OT_IMPLICIT) && !prev_order->IsType(OT_GOTO_STATION)) ||
01882 prev_order->GetDestination() != this->last_station_visited) {
01883
01884
01885
01886 int target_index = this->cur_implicit_order_index;
01887 bool found = false;
01888 while (target_index != this->cur_real_order_index) {
01889 const Order *order = this->GetOrder(target_index);
01890 if (order->IsType(OT_IMPLICIT) && order->GetDestination() == this->last_station_visited) {
01891 found = true;
01892 break;
01893 }
01894 target_index++;
01895 if (target_index >= this->orders.list->GetNumOrders()) target_index = 0;
01896 assert(target_index != this->cur_implicit_order_index);
01897 }
01898
01899 if (found) {
01900 if (suppress_implicit_orders) {
01901
01902 this->cur_implicit_order_index = target_index;
01903 InvalidateVehicleOrder(this, 0);
01904 } else {
01905
01906 const Order *order = this->GetOrder(this->cur_implicit_order_index);
01907 while (!order->IsType(OT_IMPLICIT) || order->GetDestination() != this->last_station_visited) {
01908 if (order->IsType(OT_IMPLICIT)) {
01909
01910 order = order->next;
01911 DeleteOrder(this, this->cur_implicit_order_index);
01912 } else {
01913
01914 order = order->next;
01915 this->cur_implicit_order_index++;
01916 }
01917
01918
01919 if (order == NULL) {
01920 order = this->GetOrder(0);
01921 this->cur_implicit_order_index = 0;
01922 }
01923 assert(order != NULL);
01924 }
01925 }
01926 } else if (!suppress_implicit_orders && this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID && Order::CanAllocateItem()) {
01927
01928 Order *implicit_order = new Order();
01929 implicit_order->MakeImplicit(this->last_station_visited);
01930 InsertOrder(this, implicit_order, this->cur_implicit_order_index);
01931 if (this->cur_implicit_order_index > 0) --this->cur_implicit_order_index;
01932
01933
01934
01935 uint16 &gv_flags = this->GetGroundVehicleFlags();
01936 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
01937 }
01938 }
01939 }
01940 this->current_order.MakeLoading(false);
01941 }
01942
01943 if (this->last_loading_station != INVALID_STATION &&
01944 this->last_loading_station != this->last_station_visited &&
01945 ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
01946 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0)) {
01947 IncreaseStats(Station::Get(this->last_loading_station), this, this->last_station_visited);
01948 }
01949
01950 PrepareUnload(this);
01951
01952 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
01953 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01954 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01955 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
01956
01957 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
01958 this->cur_speed = 0;
01959 this->MarkDirty();
01960 }
01961
01966 void Vehicle::CancelReservation(StationID next, Station *st)
01967 {
01968 for (Vehicle *v = this; v != NULL; v = v->next) {
01969 VehicleCargoList &cargo = v->cargo;
01970 if (cargo.ReservedCount() > 0) {
01971 DEBUG(misc, 1, "cancelling cargo reservation");
01972 GoodsEntry &ge = st->goods[v->cargo_type];
01973 cargo.Unreserve(next, &ge.cargo);
01974 SetBit(ge.acceptance_pickup, GoodsEntry::PICKUP);
01975 }
01976 }
01977 }
01978
01983 void Vehicle::LeaveStation()
01984 {
01985 assert(this->current_order.IsType(OT_LOADING));
01986
01987 delete this->cargo_payment;
01988
01989
01990 if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
01991
01992 if ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
01993 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
01994 if (this->current_order.CanLeaveWithCargo(this->last_loading_station != INVALID_STATION)) {
01995
01996
01997
01998
01999 this->RefreshNextHopsStats();
02000
02001
02002 this->last_loading_station = this->last_station_visited;
02003 } else {
02004
02005
02006
02007 this->last_loading_station = INVALID_STATION;
02008 }
02009 }
02010
02011 this->current_order.MakeLeaveStation();
02012 Station *st = Station::Get(this->last_station_visited);
02013 this->CancelReservation(INVALID_STATION, st);
02014 st->loading_vehicles.remove(this);
02015
02016 HideFillingPercent(&this->fill_percent_te_id);
02017
02018 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
02019
02020 if (IsTileType(this->tile, MP_STATION)) TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
02021
02022 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
02023 }
02024 }
02025
02032 void Vehicle::RefreshNextHopsStats()
02033 {
02034
02035 SmallMap<CargoID, uint, 1> capacities;
02036 for (Vehicle *v = this; v != NULL; v = v->Next()) {
02037 v->refit_cap = v->cargo_cap;
02038 if (v->refit_cap == 0) continue;
02039 SmallPair<CargoID, uint> *i = capacities.Find(v->cargo_type);
02040 if (i == capacities.End()) {
02041
02042 i = capacities.Append();
02043 i->first = v->cargo_type;
02044 i->second = v->cargo_cap;
02045 } else {
02046 i->second += v->cargo_cap;
02047 }
02048 }
02049
02050 uint hops = 0;
02051 const Order *first = this->orders.list->GetNextStoppingOrder(this,
02052 this->GetOrder(this->cur_implicit_order_index), hops);
02053 const Order *cur = first;
02054 const Order *next = first;
02055 while (next != NULL && cur->CanLeaveWithCargo(true)) {
02056 next = this->orders.list->GetNextStoppingOrder(this,
02057 this->orders.list->GetNext(next), ++hops);
02058 if (next == NULL) break;
02059
02060 if (next->IsType(OT_GOTO_DEPOT)) {
02061
02062 CargoID new_cid = next->GetRefitCargo();
02063 byte new_subtype = next->GetRefitSubtype();
02064 for (Vehicle *v = this; v != NULL; v = v->Next()) {
02065 const Engine *e = Engine::Get(v->engine_type);
02066 if (!HasBit(e->info.refit_mask, new_cid)) continue;
02067
02068
02069 CargoID temp_cid = v->cargo_type;
02070 byte temp_subtype = v->cargo_subtype;
02071 v->cargo_type = new_cid;
02072 v->cargo_subtype = new_subtype;
02073
02074 uint16 mail_capacity = 0;
02075 uint amount = GetVehicleCapacity(v, &mail_capacity);
02076
02077
02078 v->cargo_type = temp_cid;
02079 v->cargo_subtype = temp_subtype;
02080
02081
02082 if (new_cid != v->cargo_type && v->refit_cap > 0) {
02083 capacities[v->cargo_type] -= v->refit_cap;
02084 v->refit_cap = 0;
02085 } else if (amount < v->refit_cap) {
02086 capacities[v->cargo_type] -= v->refit_cap - amount;
02087 v->refit_cap = amount;
02088 }
02089
02090
02091 if (v->type == VEH_AIRCRAFT) {
02092 Vehicle *u = v->Next();
02093 if (mail_capacity < u->refit_cap) {
02094 capacities[u->cargo_type] -= u->refit_cap - mail_capacity;
02095 u->refit_cap = mail_capacity;
02096 }
02097 break;
02098 }
02099 if (v->type == VEH_SHIP) break;
02100 }
02101 } else {
02102 StationID next_station = next->GetDestination();
02103 Station *st = Station::GetIfValid(cur->GetDestination());
02104 if (st != NULL && next_station != INVALID_STATION && next_station != st->index) {
02105 for (const SmallPair<CargoID, uint> *i = capacities.Begin(); i != capacities.End(); ++i) {
02106
02107 if (i->second > 0) IncreaseStats(st, i->first, next_station, i->second, UINT_MAX);
02108 }
02109 }
02110 cur = next;
02111 if (cur == first) break;
02112 }
02113 }
02114
02115 for (Vehicle *v = this; v != NULL; v = v->Next()) v->refit_cap = v->cargo_cap;
02116 }
02117
02123 void Vehicle::HandleLoading(bool mode)
02124 {
02125 switch (this->current_order.GetType()) {
02126 case OT_LOADING: {
02127 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
02128
02129
02130 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
02131
02132 this->PlayLeaveStationSound();
02133
02134 this->LeaveStation();
02135
02136
02137 const Order *order = this->GetOrder(this->cur_implicit_order_index);
02138 if (order == NULL ||
02139 (!order->IsType(OT_IMPLICIT) && !order->IsType(OT_GOTO_STATION)) ||
02140 order->GetDestination() != this->last_station_visited) {
02141 return;
02142 }
02143 break;
02144 }
02145
02146 case OT_DUMMY: break;
02147
02148 default: return;
02149 }
02150
02151 this->IncrementImplicitOrderIndex();
02152 }
02153
02158 void Vehicle::GetConsistFreeCapacities(SmallMap<CargoID, uint> &capacities) const
02159 {
02160 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
02161 if (v->cargo_cap == 0) continue;
02162 SmallPair<CargoID, uint> *pair = capacities.Find(v->cargo_type);
02163 if (pair == capacities.End()) {
02164 pair = capacities.Append();
02165 pair->first = v->cargo_type;
02166 pair->second = v->cargo_cap - v->cargo.Count();
02167 } else {
02168 pair->second += v->cargo_cap - v->cargo.Count();
02169 }
02170 }
02171 }
02172
02173 uint Vehicle::GetConsistTotalCapacity() const
02174 {
02175 uint result = 0;
02176 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
02177 result += v->cargo_cap;
02178 }
02179 return result;
02180 }
02181
02188 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
02189 {
02190 CommandCost ret = CheckOwnership(this->owner);
02191 if (ret.Failed()) return ret;
02192
02193 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
02194 if (this->IsStoppedInDepot()) return CMD_ERROR;
02195
02196 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
02197 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
02198 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
02199
02200
02201
02202 if (flags & DC_EXEC) {
02203 this->current_order.SetDepotOrderType(ODTF_MANUAL);
02204 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
02205 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
02206 }
02207 return CommandCost();
02208 }
02209
02210 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR;
02211 if (flags & DC_EXEC) {
02212
02213
02214 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
02215
02216 if (this->IsGroundVehicle()) {
02217 uint16 &gv_flags = this->GetGroundVehicleFlags();
02218 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02219 }
02220
02221 this->current_order.MakeDummy();
02222 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
02223 }
02224 return CommandCost();
02225 }
02226
02227 TileIndex location;
02228 DestinationID destination;
02229 bool reverse;
02230 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};
02231 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
02232
02233 if (flags & DC_EXEC) {
02234 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
02235
02236 if (this->IsGroundVehicle()) {
02237 uint16 &gv_flags = this->GetGroundVehicleFlags();
02238 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02239 }
02240
02241 this->dest_tile = location;
02242 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
02243 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
02244 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
02245
02246
02247 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
02248
02249 if (this->type == VEH_AIRCRAFT) {
02250 Aircraft *a = Aircraft::From(this);
02251 if (a->state == FLYING && a->targetairport != destination) {
02252
02253 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
02254 AircraftNextAirportPos_and_Order(a);
02255 }
02256 }
02257 }
02258
02259 return CommandCost();
02260
02261 }
02262
02267 void Vehicle::UpdateVisualEffect(bool allow_power_change)
02268 {
02269 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02270 const Engine *e = Engine::Get(this->engine_type);
02271
02272
02273 byte visual_effect;
02274 switch (e->type) {
02275 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
02276 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
02277 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
02278 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
02279 }
02280
02281
02282 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
02283 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
02284
02285 if (callback != CALLBACK_FAILED) {
02286 callback = GB(callback, 0, 8);
02287
02288
02289 if (callback == VE_DEFAULT) {
02290 assert(HasBit(callback, VE_DISABLE_EFFECT));
02291 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
02292 }
02293 visual_effect = callback;
02294 }
02295 }
02296
02297
02298 if (visual_effect == VE_DEFAULT ||
02299 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
02300
02301
02302 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
02303 if (visual_effect == VE_DEFAULT) {
02304 visual_effect = 1 << VE_DISABLE_EFFECT;
02305 } else {
02306 SetBit(visual_effect, VE_DISABLE_EFFECT);
02307 }
02308 } else {
02309 if (visual_effect == VE_DEFAULT) {
02310
02311 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
02312 }
02313 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
02314 }
02315 }
02316
02317 this->vcache.cached_vis_effect = visual_effect;
02318
02319 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
02320 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02321 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
02322 }
02323 }
02324
02325 static const int8 _vehicle_smoke_pos[8] = {
02326 1, 1, 1, 0, -1, -1, -1, 0
02327 };
02328
02333 void Vehicle::ShowVisualEffect() const
02334 {
02335 assert(this->IsPrimaryVehicle());
02336 bool sound = false;
02337
02338
02339
02340
02341
02342
02343 if (_settings_game.vehicle.smoke_amount == 0 ||
02344 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
02345 this->cur_speed < 2) {
02346 return;
02347 }
02348 if (this->type == VEH_TRAIN) {
02349 const Train *t = Train::From(this);
02350
02351
02352
02353
02354 if (HasBit(t->flags, VRF_REVERSING) ||
02355 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
02356 t->cur_speed >= t->Train::GetCurrentMaxSpeed())) {
02357 return;
02358 }
02359 }
02360
02361 const Vehicle *v = this;
02362
02363 do {
02364 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
02365 byte effect_type = GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
02366 bool disable_effect = HasBit(v->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
02367
02368
02369
02370
02371
02372
02373
02374
02375 if (disable_effect ||
02376 v->vehstatus & VS_HIDDEN ||
02377 (MayHaveBridgeAbove(v->tile) && IsBridgeAbove(v->tile)) ||
02378 IsDepotTile(v->tile) ||
02379 IsTunnelTile(v->tile) ||
02380 (v->type == VEH_TRAIN &&
02381 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
02382 continue;
02383 }
02384
02385 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
02386 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
02387
02388 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
02389 x = -x;
02390 y = -y;
02391 }
02392
02393 switch (effect_type) {
02394 case VE_TYPE_STEAM:
02395
02396
02397
02398
02399
02400 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / this->vcache.cached_max_speed))) == 0) {
02401 CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
02402 sound = true;
02403 }
02404 break;
02405
02406 case VE_TYPE_DIESEL: {
02407
02408
02409
02410
02411
02412
02413
02414
02415
02416
02417
02418 int power_weight_effect = 0;
02419 if (v->type == VEH_TRAIN) {
02420 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
02421 }
02422 if (this->cur_speed < (this->vcache.cached_max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
02423 Chance16((64 - ((this->cur_speed << 5) / this->vcache.cached_max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
02424 CreateEffectVehicleRel(v, x, y, 10, EV_DIESEL_SMOKE);
02425 sound = true;
02426 }
02427 break;
02428 }
02429
02430 case VE_TYPE_ELECTRIC:
02431
02432
02433
02434
02435
02436
02437 if (GB(v->tick_counter, 0, 2) == 0 &&
02438 Chance16((6 - ((this->cur_speed << 2) / this->vcache.cached_max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
02439 CreateEffectVehicleRel(v, x, y, 10, EV_ELECTRIC_SPARK);
02440 sound = true;
02441 }
02442 break;
02443
02444 default:
02445 break;
02446 }
02447 } while ((v = v->Next()) != NULL);
02448
02449 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
02450 }
02451
02456 void Vehicle::SetNext(Vehicle *next)
02457 {
02458 assert(this != next);
02459
02460 if (this->next != NULL) {
02461
02462 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02463 v->first = this->next;
02464 }
02465 this->next->previous = NULL;
02466 }
02467
02468 this->next = next;
02469
02470 if (this->next != NULL) {
02471
02472 if (this->next->previous != NULL) this->next->previous->next = NULL;
02473 this->next->previous = this;
02474 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02475 v->first = this->first;
02476 }
02477 }
02478 }
02479
02485 void Vehicle::AddToShared(Vehicle *shared_chain)
02486 {
02487 assert(this->previous_shared == NULL && this->next_shared == NULL);
02488
02489 if (shared_chain->orders.list == NULL) {
02490 assert(shared_chain->previous_shared == NULL);
02491 assert(shared_chain->next_shared == NULL);
02492 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
02493 }
02494
02495 this->next_shared = shared_chain->next_shared;
02496 this->previous_shared = shared_chain;
02497
02498 shared_chain->next_shared = this;
02499
02500 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
02501
02502 shared_chain->orders.list->AddVehicle(this);
02503 }
02504
02508 void Vehicle::RemoveFromShared()
02509 {
02510
02511
02512 bool were_first = (this->FirstShared() == this);
02513 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
02514
02515 this->orders.list->RemoveVehicle(this);
02516
02517 if (!were_first) {
02518
02519 this->previous_shared->next_shared = this->NextShared();
02520 }
02521
02522 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
02523
02524
02525 if (this->orders.list->GetNumVehicles() == 1) {
02526
02527 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
02528 InvalidateVehicleOrder(this->FirstShared(), 0);
02529 } else if (were_first) {
02530
02531
02532 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
02533 }
02534
02535 this->next_shared = NULL;
02536 this->previous_shared = NULL;
02537 }
02538
02539 void VehiclesYearlyLoop()
02540 {
02541 Vehicle *v;
02542 FOR_ALL_VEHICLES(v) {
02543 if (v->IsPrimaryVehicle()) {
02544
02545 Money profit = v->GetDisplayProfitThisYear();
02546 if (v->age >= 730 && profit < 0) {
02547 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
02548 SetDParam(0, v->index);
02549 SetDParam(1, profit);
02550 AddVehicleNewsItem(
02551 STR_NEWS_VEHICLE_IS_UNPROFITABLE,
02552 NS_ADVICE,
02553 v->index
02554 );
02555 }
02556 AI::NewEvent(v->owner, new AIEventVehicleUnprofitable(v->index));
02557 }
02558
02559 v->profit_last_year = v->profit_this_year;
02560 v->profit_this_year = 0;
02561 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
02562 }
02563 }
02564 }
02565
02566
02576 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
02577 {
02578 const Engine *e = Engine::GetIfValid(engine_type);
02579 assert(e != NULL);
02580
02581 switch (e->type) {
02582 case VEH_TRAIN:
02583 return (st->facilities & FACIL_TRAIN) != 0;
02584
02585 case VEH_ROAD:
02586
02587
02588
02589 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
02590
02591 case VEH_SHIP:
02592 return (st->facilities & FACIL_DOCK) != 0;
02593
02594 case VEH_AIRCRAFT:
02595 return (st->facilities & FACIL_AIRPORT) != 0 &&
02596 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
02597
02598 default:
02599 return false;
02600 }
02601 }
02602
02609 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
02610 {
02611 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
02612
02613 return CanVehicleUseStation(v->engine_type, st);
02614 }
02615
02621 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
02622 {
02623 assert(this->IsGroundVehicle());
02624 if (this->type == VEH_TRAIN) {
02625 return &Train::From(this)->gcache;
02626 } else {
02627 return &RoadVehicle::From(this)->gcache;
02628 }
02629 }
02630
02636 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
02637 {
02638 assert(this->IsGroundVehicle());
02639 if (this->type == VEH_TRAIN) {
02640 return &Train::From(this)->gcache;
02641 } else {
02642 return &RoadVehicle::From(this)->gcache;
02643 }
02644 }
02645
02651 uint16 &Vehicle::GetGroundVehicleFlags()
02652 {
02653 assert(this->IsGroundVehicle());
02654 if (this->type == VEH_TRAIN) {
02655 return Train::From(this)->gv_flags;
02656 } else {
02657 return RoadVehicle::From(this)->gv_flags;
02658 }
02659 }
02660
02666 const uint16 &Vehicle::GetGroundVehicleFlags() const
02667 {
02668 assert(this->IsGroundVehicle());
02669 if (this->type == VEH_TRAIN) {
02670 return Train::From(this)->gv_flags;
02671 } else {
02672 return RoadVehicle::From(this)->gv_flags;
02673 }
02674 }
02675
02684 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
02685 {
02686 if (v->type == VEH_TRAIN) {
02687 Train *u = Train::From(v);
02688
02689 u = u->GetFirstEnginePart();
02690
02691
02692 for (; u != NULL && num_vehicles > 0; num_vehicles--) {
02693 do {
02694
02695 set.Include(u->index);
02696
02697
02698 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
02699
02700 u = u->Next();
02701 } while (u != NULL && u->IsArticulatedPart());
02702 }
02703 }
02704 }