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 _vehicle_pool.CleanPool();
00590 _cargo_payment_pool.CleanPool();
00591
00592 _age_cargo_skip_counter = 1;
00593
00594 _vehicles_to_autoreplace.Reset();
00595 ResetVehiclePosHash();
00596 }
00597
00598 uint CountVehiclesInChain(const Vehicle *v)
00599 {
00600 uint count = 0;
00601 do count++; while ((v = v->Next()) != NULL);
00602 return count;
00603 }
00604
00610 void CountCompanyVehicles(CompanyID cid, uint counts[4])
00611 {
00612 for (uint i = 0; i < 4; i++) counts[i] = 0;
00613
00614 const Vehicle *v;
00615 FOR_ALL_VEHICLES(v) {
00616 if (v->owner == cid && v->IsPrimaryVehicle()) counts[v->type]++;
00617 }
00618 }
00619
00624 bool Vehicle::IsEngineCountable() const
00625 {
00626 switch (this->type) {
00627 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
00628 case VEH_TRAIN:
00629 return !this->IsArticulatedPart() &&
00630 !Train::From(this)->IsRearDualheaded();
00631 case VEH_ROAD: return RoadVehicle::From(this)->IsFrontEngine();
00632 case VEH_SHIP: return true;
00633 default: return false;
00634 }
00635 }
00636
00644 void Vehicle::HandlePathfindingResult(bool path_found)
00645 {
00646 if (path_found) {
00647
00648 if (!HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00649
00650
00651 ClrBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00652
00653 DeleteVehicleNews(this->index, STR_NEWS_VEHICLE_IS_LOST);
00654 return;
00655 }
00656
00657
00658 if (HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00659
00660
00661 SetBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00662
00663 AI::NewEvent(this->owner, new AIEventVehicleLost(this->index));
00664 if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) {
00665 SetDParam(0, this->index);
00666 AddVehicleNewsItem(STR_NEWS_VEHICLE_IS_LOST, NS_ADVICE, this->index);
00667 }
00668 }
00669
00671 void Vehicle::PreDestructor()
00672 {
00673 if (CleaningPool()) return;
00674
00675 if (Station::IsValidID(this->last_station_visited)) {
00676 Station::Get(this->last_station_visited)->loading_vehicles.remove(this);
00677
00678 HideFillingPercent(&this->fill_percent_te_id);
00679
00680 delete this->cargo_payment;
00681 }
00682
00683 if (this->IsEngineCountable()) {
00684 Company::Get(this->owner)->num_engines[this->engine_type]--;
00685 if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
00686
00687 DeleteGroupHighlightOfVehicle(this);
00688 if (Group::IsValidID(this->group_id)) Group::Get(this->group_id)->num_engines[this->engine_type]--;
00689 if (this->IsPrimaryVehicle()) DecreaseGroupNumVehicle(this->group_id);
00690 }
00691
00692 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
00693 Aircraft *a = Aircraft::From(this);
00694 Station *st = GetTargetAirportIfValid(a);
00695 if (st != NULL) {
00696 const AirportFTA *layout = st->airport.GetFTA()->layout;
00697 CLRBITS(st->airport.flags, layout[a->previous_pos].block | layout[a->pos].block);
00698 }
00699 }
00700
00701
00702 if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
00703 RoadVehicle *v = RoadVehicle::From(this);
00704 if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
00705
00706 RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
00707 }
00708 }
00709
00710 if (this->Previous() == NULL) {
00711 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
00712 }
00713
00714 if (this->IsPrimaryVehicle()) {
00715 DeleteWindowById(WC_VEHICLE_VIEW, this->index);
00716 DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
00717 DeleteWindowById(WC_VEHICLE_REFIT, this->index);
00718 DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
00719 DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
00720 SetWindowDirty(WC_COMPANY, this->owner);
00721 OrderBackup::ClearVehicle(this);
00722 }
00723 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00724
00725 this->cargo.Truncate(0);
00726 DeleteVehicleOrders(this);
00727 DeleteDepotHighlightOfVehicle(this);
00728
00729 extern void StopGlobalFollowVehicle(const Vehicle *v);
00730 StopGlobalFollowVehicle(this);
00731
00732 ReleaseDisastersTargetingVehicle(this->index);
00733 }
00734
00735 Vehicle::~Vehicle()
00736 {
00737 free(this->name);
00738
00739 if (CleaningPool()) return;
00740
00741
00742
00743 if (!(this->vehstatus & VS_HIDDEN)) MarkSingleVehicleDirty(this);
00744
00745 Vehicle *v = this->Next();
00746 this->SetNext(NULL);
00747
00748 delete v;
00749
00750 UpdateVehiclePosHash(this, INVALID_COORD, 0);
00751 DeleteVehicleNews(this->index, INVALID_STRING_ID);
00752 DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index);
00753 }
00754
00759 void VehicleEnteredDepotThisTick(Vehicle *v)
00760 {
00761
00762 _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
00763
00764
00765
00766
00767
00768
00769 v->vehstatus |= VS_STOPPED;
00770 }
00771
00777 static void RunVehicleDayProc()
00778 {
00779 if (_game_mode != GM_NORMAL) return;
00780
00781
00782 for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
00783 Vehicle *v = Vehicle::Get(i);
00784 if (v == NULL) continue;
00785
00786
00787 if ((v->day_counter & 0x1F) == 0) {
00788 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
00789 if (callback != CALLBACK_FAILED) {
00790 if (HasBit(callback, 0)) TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32);
00791 if (HasBit(callback, 1)) v->colourmap = PAL_NONE;
00792 }
00793 }
00794
00795
00796 v->OnNewDay();
00797 }
00798 }
00799
00800 void CallVehicleTicks()
00801 {
00802 _vehicles_to_autoreplace.Clear();
00803
00804 _age_cargo_skip_counter = (_age_cargo_skip_counter == 0) ? 184 : (_age_cargo_skip_counter - 1);
00805
00806 RunVehicleDayProc();
00807
00808 Station *st;
00809 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
00810
00811 Vehicle *v;
00812 FOR_ALL_VEHICLES(v) {
00813
00814 if (!v->Tick()) {
00815 assert(Vehicle::Get(vehicle_index) == NULL);
00816 continue;
00817 }
00818
00819 assert(Vehicle::Get(vehicle_index) == v);
00820
00821 switch (v->type) {
00822 default: break;
00823
00824 case VEH_TRAIN:
00825 case VEH_ROAD:
00826 case VEH_AIRCRAFT:
00827 case VEH_SHIP:
00828 if (_age_cargo_skip_counter == 0) v->cargo.AgeCargo();
00829
00830 if (v->type == VEH_TRAIN && Train::From(v)->IsWagon()) continue;
00831 if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
00832 if (v->type == VEH_ROAD && !RoadVehicle::From(v)->IsFrontEngine()) continue;
00833
00834 v->motion_counter += v->cur_speed;
00835
00836 if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
00837
00838
00839 if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
00840 }
00841 }
00842
00843 Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
00844 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
00845 v = it->first;
00846
00847 cur_company.Change(v->owner);
00848
00849
00850
00851
00852 if (it->second) v->vehstatus &= ~VS_STOPPED;
00853
00854
00855 int x = v->x_pos;
00856 int y = v->y_pos;
00857 int z = v->z_pos;
00858
00859 const Company *c = Company::Get(_current_company);
00860 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
00861 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
00862 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
00863
00864 if (!IsLocalCompany()) continue;
00865
00866 if (res.Succeeded()) {
00867 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
00868 continue;
00869 }
00870
00871 StringID error_message = res.GetErrorMessage();
00872 if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
00873
00874 if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
00875
00876 StringID message;
00877 if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
00878 message = error_message;
00879 } else {
00880 message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
00881 }
00882
00883 SetDParam(0, v->index);
00884 SetDParam(1, error_message);
00885 AddVehicleNewsItem(message, NS_ADVICE, v->index);
00886 }
00887
00888 cur_company.Restore();
00889 }
00890
00895 static void DoDrawVehicle(const Vehicle *v)
00896 {
00897 SpriteID image = v->cur_image;
00898 PaletteID pal = PAL_NONE;
00899
00900 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
00901 if ((v->vehstatus & VS_SHADOW) != 0)
00902 {
00903 SetBit(image, PALETTE_MODIFIER_SHADOW);
00904 }
00905 AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
00906 v->x_extent, v->y_extent, v->z_extent, v->z_pos, false);
00907 }
00908
00913 void ViewportAddVehicles(DrawPixelInfo *dpi)
00914 {
00915
00916 const int l = dpi->left;
00917 const int r = dpi->left + dpi->width;
00918 const int t = dpi->top;
00919 const int b = dpi->top + dpi->height;
00920
00921
00922 int xl, xu, yl, yu;
00923
00924 if (dpi->width + 70 < (1 << (7 + 6))) {
00925 xl = GB(l - 70, 7, 6);
00926 xu = GB(r, 7, 6);
00927 } else {
00928
00929 xl = 0;
00930 xu = 0x3F;
00931 }
00932
00933 if (dpi->height + 70 < (1 << (6 + 6))) {
00934 yl = GB(t - 70, 6, 6) << 6;
00935 yu = GB(b, 6, 6) << 6;
00936 } else {
00937
00938 yl = 0;
00939 yu = 0x3F << 6;
00940 }
00941
00942 for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
00943 for (int x = xl;; x = (x + 1) & 0x3F) {
00944 const Vehicle *v = _vehicle_position_hash[x + y];
00945
00946 while (v != NULL) {
00947 if (!(v->vehstatus & VS_HIDDEN) ) {
00948 DoDrawVehicle(v);
00949 }
00950 v = v->next_hash;
00951 }
00952
00953 if (x == xu) break;
00954 }
00955
00956 if (y == yu) break;
00957 }
00958 }
00959
00967 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
00968 {
00969 Vehicle *found = NULL, *v;
00970 uint dist, best_dist = UINT_MAX;
00971
00972 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
00973
00974 x = ScaleByZoom(x + vp->virtual_left, vp->zoom);
00975 y = ScaleByZoom(y + vp->virtual_top, vp->zoom) ;
00976
00977 FOR_ALL_VEHICLES(v) {
00978 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
00979 x >= v->coord.left && x <= v->coord.right &&
00980 y >= v->coord.top && y <= v->coord.bottom) {
00981
00982 dist = max(
00983 abs(((v->coord.left + v->coord.right) >> 1) - x),
00984 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
00985 );
00986
00987 if (dist < best_dist) {
00988 found = v;
00989 best_dist = dist;
00990 }
00991 }
00992 }
00993
00994 return found;
00995 }
00996
01001 void DecreaseVehicleValue(Vehicle *v)
01002 {
01003 v->value -= v->value >> 8;
01004 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01005 }
01006
01007 static const byte _breakdown_chance[64] = {
01008 3, 3, 3, 3, 3, 3, 3, 3,
01009 4, 4, 5, 5, 6, 6, 7, 7,
01010 8, 8, 9, 9, 10, 10, 11, 11,
01011 12, 13, 13, 13, 13, 14, 15, 16,
01012 17, 19, 21, 25, 28, 31, 34, 37,
01013 40, 44, 48, 52, 56, 60, 64, 68,
01014 72, 80, 90, 100, 110, 120, 130, 140,
01015 150, 170, 190, 210, 230, 250, 250, 250,
01016 };
01017
01018 void CheckVehicleBreakdown(Vehicle *v)
01019 {
01020 int rel, rel_old;
01021
01022
01023 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
01024 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01025
01026 if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
01027 _settings_game.difficulty.vehicle_breakdowns < 1 ||
01028 v->cur_speed < 5 || _game_mode == GM_MENU) {
01029 return;
01030 }
01031
01032 uint32 r = Random();
01033
01034
01035 int chance = v->breakdown_chance + 1;
01036 if (Chance16I(1, 25, r)) chance += 25;
01037 v->breakdown_chance = min(255, chance);
01038
01039
01040 rel = v->reliability;
01041 if (v->type == VEH_SHIP) rel += 0x6666;
01042
01043
01044 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
01045
01046
01047 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
01048 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
01049 v->breakdown_delay = GB(r, 24, 7) + 0x80;
01050 v->breakdown_chance = 0;
01051 }
01052 }
01053
01060 bool Vehicle::HandleBreakdown()
01061 {
01062
01063
01064
01065
01066
01067 switch (this->breakdown_ctr) {
01068 case 0:
01069 return false;
01070
01071 case 2:
01072 this->breakdown_ctr = 1;
01073
01074 if (this->breakdowns_since_last_service != 255) {
01075 this->breakdowns_since_last_service++;
01076 }
01077
01078 this->MarkDirty();
01079 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01080 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01081
01082 if (this->type == VEH_AIRCRAFT) {
01083
01084 this->vehstatus |= VS_AIRCRAFT_BROKEN;
01085 } else {
01086 this->cur_speed = 0;
01087
01088 if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
01089 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
01090 (this->type == VEH_TRAIN ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
01091 (this->type == VEH_TRAIN ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
01092 }
01093
01094 if (!(this->vehstatus & VS_HIDDEN)) {
01095 EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
01096 if (u != NULL) u->animation_state = this->breakdown_delay * 2;
01097 }
01098 }
01099
01100 case 1:
01101
01102 if (this->type == VEH_AIRCRAFT) return false;
01103
01104
01105 if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
01106 if (--this->breakdown_delay == 0) {
01107 this->breakdown_ctr = 0;
01108 this->MarkDirty();
01109 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01110 }
01111 }
01112 return true;
01113
01114 default:
01115 if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
01116 return false;
01117 }
01118 }
01119
01124 void AgeVehicle(Vehicle *v)
01125 {
01126 if (v->age < MAX_DAY) v->age++;
01127
01128 int age = v->age - v->max_age;
01129 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
01130 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
01131 v->reliability_spd_dec <<= 1;
01132 }
01133
01134 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01135
01136
01137 if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
01138
01139
01140 if (Company::Get(v->owner)->settings.engine_renew && Engine::Get(v->engine_type)->company_avail != 0) return;
01141
01142 StringID str;
01143 if (age == -DAYS_IN_LEAP_YEAR) {
01144 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
01145 } else if (age == 0) {
01146 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
01147 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
01148 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
01149 } else {
01150 return;
01151 }
01152
01153 SetDParam(0, v->index);
01154 AddVehicleNewsItem(str, NS_ADVICE, v->index);
01155 }
01156
01163 uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *colour)
01164 {
01165 int count = 0;
01166 int max = 0;
01167 int cars = 0;
01168 int unloading = 0;
01169 bool loading = false;
01170
01171 const Vehicle *u = v;
01172
01173 const Station *st = Station::GetIfValid(v->last_station_visited);
01174 assert(colour == NULL || st != NULL);
01175
01176
01177 for (; v != NULL; v = v->Next()) {
01178 count += v->cargo.Count();
01179 max += v->cargo_cap;
01180 if (v->cargo_cap != 0 && colour != NULL) {
01181 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
01182 loading |= !(u->current_order.GetLoadType() & OLFB_NO_LOAD) && st->goods[v->cargo_type].days_since_pickup != 255;
01183 cars++;
01184 }
01185 }
01186
01187 if (colour != NULL) {
01188 if (unloading == 0 && loading) {
01189 *colour = STR_PERCENT_UP;
01190 } else if (cars == unloading || !loading) {
01191 *colour = STR_PERCENT_DOWN;
01192 } else {
01193 *colour = STR_PERCENT_UP_DOWN;
01194 }
01195 }
01196
01197
01198 if (max == 0) return 100;
01199
01200
01201 return (count * 100) / max;
01202 }
01203
01208 void VehicleEnterDepot(Vehicle *v)
01209 {
01210
01211 assert(v == v->First());
01212
01213 switch (v->type) {
01214 case VEH_TRAIN: {
01215 Train *t = Train::From(v);
01216 SetWindowClassesDirty(WC_TRAINS_LIST);
01217
01218 SetDepotReservation(t->tile, false);
01219 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
01220
01221 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
01222 t->wait_counter = 0;
01223 t->force_proceed = TFP_NONE;
01224 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
01225 t->ConsistChanged(true);
01226 break;
01227 }
01228
01229 case VEH_ROAD:
01230 SetWindowClassesDirty(WC_ROADVEH_LIST);
01231 break;
01232
01233 case VEH_SHIP: {
01234 SetWindowClassesDirty(WC_SHIPS_LIST);
01235 Ship *ship = Ship::From(v);
01236 ship->state = TRACK_BIT_DEPOT;
01237 ship->UpdateCache();
01238 ship->UpdateViewport(true, true);
01239 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01240 break;
01241 }
01242
01243 case VEH_AIRCRAFT:
01244 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01245 HandleAircraftEnterHangar(Aircraft::From(v));
01246 break;
01247 default: NOT_REACHED();
01248 }
01249 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01250
01251 if (v->type != VEH_TRAIN) {
01252
01253
01254 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01255 }
01256 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01257
01258 v->vehstatus |= VS_HIDDEN;
01259 v->cur_speed = 0;
01260
01261 VehicleServiceInDepot(v);
01262
01263 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01264
01265 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01266 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01267
01268 const Order *real_order = v->GetOrder(v->cur_real_order_index);
01269 Order t = v->current_order;
01270 v->current_order.MakeDummy();
01271
01272
01273
01274 if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
01275 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
01276 (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
01277
01278 return;
01279 }
01280
01281 if (t.IsRefit()) {
01282 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01283 CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
01284 cur_company.Restore();
01285
01286 if (cost.Failed()) {
01287 _vehicles_to_autoreplace[v] = false;
01288 if (v->owner == _local_company) {
01289
01290 SetDParam(0, v->index);
01291 AddVehicleNewsItem(STR_NEWS_ORDER_REFIT_FAILED, NS_ADVICE, v->index);
01292 }
01293 } else if (cost.GetCost() != 0) {
01294 v->profit_this_year -= cost.GetCost() << 8;
01295 if (v->owner == _local_company) {
01296 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01297 }
01298 }
01299 }
01300
01301 if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01302
01303 v->DeleteUnreachedAutoOrders();
01304 UpdateVehicleTimetable(v, true);
01305 v->IncrementAutoOrderIndex();
01306 }
01307 if (t.GetDepotActionType() & ODATFB_HALT) {
01308
01309 _vehicles_to_autoreplace[v] = false;
01310 if (v->owner == _local_company) {
01311 SetDParam(0, v->index);
01312 AddVehicleNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, NS_ADVICE, v->index);
01313 }
01314 AI::NewEvent(v->owner, new AIEventVehicleWaitingInDepot(v->index));
01315 }
01316 }
01317 }
01318
01319
01327 void VehicleMove(Vehicle *v, bool update_viewport)
01328 {
01329 int img = v->cur_image;
01330 Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
01331 const Sprite *spr = GetSprite(img, ST_NORMAL);
01332
01333 pt.x += spr->x_offs;
01334 pt.y += spr->y_offs;
01335
01336 UpdateVehiclePosHash(v, pt.x, pt.y);
01337
01338 Rect old_coord = v->coord;
01339 v->coord.left = pt.x;
01340 v->coord.top = pt.y;
01341 v->coord.right = pt.x + spr->width + 2;
01342 v->coord.bottom = pt.y + spr->height + 2;
01343
01344 if (update_viewport) {
01345 MarkAllViewportsDirty(
01346 min(old_coord.left, v->coord.left),
01347 min(old_coord.top, v->coord.top),
01348 max(old_coord.right, v->coord.right) + 1,
01349 max(old_coord.bottom, v->coord.bottom) + 1
01350 );
01351 }
01352 }
01353
01362 void MarkSingleVehicleDirty(const Vehicle *v)
01363 {
01364 MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1, v->coord.bottom + 1);
01365 }
01366
01372 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01373 {
01374 static const int8 _delta_coord[16] = {
01375 -1,-1,-1, 0, 1, 1, 1, 0,
01376 -1, 0, 1, 1, 1, 0,-1,-1,
01377 };
01378
01379 int x = v->x_pos + _delta_coord[v->direction];
01380 int y = v->y_pos + _delta_coord[v->direction + 8];
01381
01382 GetNewVehiclePosResult gp;
01383 gp.x = x;
01384 gp.y = y;
01385 gp.old_tile = v->tile;
01386 gp.new_tile = TileVirtXY(x, y);
01387 return gp;
01388 }
01389
01390 static const Direction _new_direction_table[] = {
01391 DIR_N, DIR_NW, DIR_W,
01392 DIR_NE, DIR_SE, DIR_SW,
01393 DIR_E, DIR_SE, DIR_S
01394 };
01395
01396 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01397 {
01398 int i = 0;
01399
01400 if (y >= v->y_pos) {
01401 if (y != v->y_pos) i += 3;
01402 i += 3;
01403 }
01404
01405 if (x >= v->x_pos) {
01406 if (x != v->x_pos) i++;
01407 i++;
01408 }
01409
01410 Direction dir = v->direction;
01411
01412 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01413 if (dirdiff == DIRDIFF_SAME) return dir;
01414 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01415 }
01416
01426 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01427 {
01428 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01429 }
01430
01438 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01439 {
01440
01441 const Vehicle *v;
01442 FOR_ALL_VEHICLES(v) {
01443 if (v->type == type && v->owner == owner) {
01444 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01445 }
01446 }
01447
01448 if (this->maxid == 0) return;
01449
01450
01451
01452
01453 this->cache = CallocT<bool>(this->maxid + 2);
01454
01455
01456 FOR_ALL_VEHICLES(v) {
01457 if (v->type == type && v->owner == owner) {
01458 this->cache[v->unitnumber] = true;
01459 }
01460 }
01461 }
01462
01464 UnitID FreeUnitIDGenerator::NextID()
01465 {
01466 if (this->maxid <= this->curid) return ++this->curid;
01467
01468 while (this->cache[++this->curid]) { }
01469
01470 return this->curid;
01471 }
01472
01478 UnitID GetFreeUnitNumber(VehicleType type)
01479 {
01480
01481 uint max_veh;
01482 switch (type) {
01483 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
01484 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
01485 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
01486 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
01487 default: NOT_REACHED();
01488 }
01489
01490 uint amounts[4];
01491 CountCompanyVehicles(_current_company, amounts);
01492 assert((uint)type < lengthof(amounts));
01493 if (amounts[type] >= max_veh) return UINT16_MAX;
01494
01495 FreeUnitIDGenerator gen(type, _current_company);
01496
01497 return gen.NextID();
01498 }
01499
01500
01509 bool CanBuildVehicleInfrastructure(VehicleType type)
01510 {
01511 assert(IsCompanyBuildableVehicleType(type));
01512
01513 if (!Company::IsValidID(_local_company)) return false;
01514 if (!_settings_client.gui.disable_unsuitable_building) return true;
01515
01516 UnitID max;
01517 switch (type) {
01518 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
01519 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
01520 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
01521 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01522 default: NOT_REACHED();
01523 }
01524
01525
01526 if (max > 0) {
01527
01528 const Engine *e;
01529 FOR_ALL_ENGINES_OF_TYPE(e, type) {
01530 if (HasBit(e->company_avail, _local_company)) return true;
01531 }
01532 return false;
01533 }
01534
01535
01536 const Vehicle *v;
01537 FOR_ALL_VEHICLES(v) {
01538 if (v->owner == _local_company && v->type == type) return true;
01539 }
01540
01541 return false;
01542 }
01543
01544
01552 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
01553 {
01554 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01555 const Engine *e = Engine::Get(engine_type);
01556 switch (e->type) {
01557 default: NOT_REACHED();
01558 case VEH_TRAIN:
01559 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
01560
01561
01562 engine_type = parent_engine_type;
01563 e = Engine::Get(engine_type);
01564
01565 }
01566
01567 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01568 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01569 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
01570 if (!CargoSpec::Get(cargo_type)->is_freight) {
01571 if (parent_engine_type == INVALID_ENGINE) {
01572 return LS_PASSENGER_WAGON_STEAM;
01573 } else {
01574 switch (RailVehInfo(parent_engine_type)->engclass) {
01575 default: NOT_REACHED();
01576 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
01577 case EC_DIESEL: return LS_PASSENGER_WAGON_DIESEL;
01578 case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
01579 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
01580 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
01581 }
01582 }
01583 } else {
01584 return LS_FREIGHT_WAGON;
01585 }
01586 } else {
01587 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
01588
01589 switch (e->u.rail.engclass) {
01590 default: NOT_REACHED();
01591 case EC_STEAM: return LS_STEAM;
01592 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
01593 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
01594 case EC_MONORAIL: return LS_MONORAIL;
01595 case EC_MAGLEV: return LS_MAGLEV;
01596 }
01597 }
01598
01599 case VEH_ROAD:
01600
01601 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01602 engine_type = parent_engine_type;
01603 e = Engine::Get(engine_type);
01604 cargo_type = v->First()->cargo_type;
01605 }
01606 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01607 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01608
01609
01610 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
01611
01612 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01613 } else {
01614
01615 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01616 }
01617
01618 case VEH_SHIP:
01619 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01620 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01621 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01622
01623 case VEH_AIRCRAFT:
01624 switch (e->u.air.subtype) {
01625 case AIR_HELI: return LS_HELICOPTER;
01626 case AIR_CTOL: return LS_SMALL_PLANE;
01627 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
01628 default: NOT_REACHED();
01629 }
01630 }
01631 }
01632
01642 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
01643 {
01644 const Company *c = Company::Get(company);
01645 LiveryScheme scheme = LS_DEFAULT;
01646
01647
01648
01649 if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
01650
01651 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
01652
01653
01654 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01655 }
01656
01657 return &c->livery[scheme];
01658 }
01659
01660
01661 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01662 {
01663 PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01664
01665
01666 if (map != PAL_NONE) return map;
01667
01668 const Engine *e = Engine::Get(engine_type);
01669
01670
01671 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
01672 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01673
01674 if (callback != CALLBACK_FAILED) {
01675 assert_compile(PAL_NONE == 0);
01676 map = GB(callback, 0, 14);
01677
01678
01679 if (!HasBit(callback, 14)) {
01680
01681 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01682 return map;
01683 }
01684 }
01685 }
01686
01687 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
01688
01689 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
01690
01691
01692 if (!Company::IsValidID(company)) return map;
01693
01694 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
01695
01696 map += livery->colour1;
01697 if (twocc) map += livery->colour2 * 16;
01698
01699
01700 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01701 return map;
01702 }
01703
01710 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
01711 {
01712 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01713 }
01714
01720 PaletteID GetVehiclePalette(const Vehicle *v)
01721 {
01722 if (v->IsGroundVehicle()) {
01723 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
01724 }
01725
01726 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01727 }
01728
01737 uint GetVehicleCapacity(const Vehicle *v, uint16 *mail_capacity)
01738 {
01739 if (mail_capacity != NULL) *mail_capacity = 0;
01740 const Engine *e = Engine::Get(v->engine_type);
01741
01742 if (!e->CanCarryCargo()) return 0;
01743
01744 if (mail_capacity != NULL && e->type == VEH_AIRCRAFT && IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
01745 *mail_capacity = GetVehicleProperty(v, PROP_AIRCRAFT_MAIL_CAPACITY, e->u.air.mail_capacity);
01746 }
01747 CargoID default_cargo = e->GetDefaultCargoType();
01748
01749
01750
01751 if (HasBit(e->info.callback_mask, CBM_VEHICLE_REFIT_CAPACITY) &&
01752 (default_cargo != v->cargo_type || v->cargo_subtype != 0)) {
01753 uint16 callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
01754 if (callback != CALLBACK_FAILED) return callback;
01755 }
01756
01757
01758 uint capacity;
01759 switch (e->type) {
01760 case VEH_TRAIN: capacity = GetVehicleProperty(v, PROP_TRAIN_CARGO_CAPACITY, e->u.rail.capacity); break;
01761 case VEH_ROAD: capacity = GetVehicleProperty(v, PROP_ROADVEH_CARGO_CAPACITY, e->u.road.capacity); break;
01762 case VEH_SHIP: capacity = GetVehicleProperty(v, PROP_SHIP_CARGO_CAPACITY, e->u.ship.capacity); break;
01763 case VEH_AIRCRAFT: capacity = GetVehicleProperty(v, PROP_AIRCRAFT_PASSENGER_CAPACITY, e->u.air.passenger_capacity); break;
01764 default: NOT_REACHED();
01765 }
01766
01767
01768
01769 if (e->type != VEH_SHIP) {
01770 if (e->type == VEH_AIRCRAFT) {
01771 if (!IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
01772 capacity += GetVehicleProperty(v, PROP_AIRCRAFT_MAIL_CAPACITY, e->u.air.mail_capacity);
01773 }
01774 if (v->cargo_type == CT_MAIL) return capacity;
01775 } else {
01776 switch (default_cargo) {
01777 case CT_PASSENGERS: break;
01778 case CT_MAIL:
01779 case CT_GOODS: capacity *= 2; break;
01780 default: capacity *= 4; break;
01781 }
01782 }
01783 switch (v->cargo_type) {
01784 case CT_PASSENGERS: break;
01785 case CT_MAIL:
01786 case CT_GOODS: capacity /= 2; break;
01787 default: capacity /= 4; break;
01788 }
01789 }
01790
01791 return capacity;
01792 }
01793
01797 void Vehicle::DeleteUnreachedAutoOrders()
01798 {
01799 const Order *order = this->GetOrder(this->cur_auto_order_index);
01800 while (order != NULL) {
01801 if (this->cur_auto_order_index == this->cur_real_order_index) break;
01802
01803 if (order->IsType(OT_AUTOMATIC)) {
01804
01805 order = order->next;
01806 DeleteOrder(this, this->cur_auto_order_index);
01807 } else {
01808
01809 order = order->next;
01810 this->cur_auto_order_index++;
01811 }
01812
01813
01814 if (order == NULL) {
01815 order = this->GetOrder(0);
01816 this->cur_auto_order_index = 0;
01817 }
01818 }
01819 }
01820
01825 void Vehicle::BeginLoading()
01826 {
01827 assert(IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP);
01828
01829 if (this->current_order.IsType(OT_GOTO_STATION) &&
01830 this->current_order.GetDestination() == this->last_station_visited) {
01831 this->DeleteUnreachedAutoOrders();
01832
01833
01834 this->current_order.MakeLoading(true);
01835 UpdateVehicleTimetable(this, true);
01836
01837
01838
01839
01840
01841
01842 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
01843
01844 } else {
01845
01846
01847
01848 Order *in_list = this->GetOrder(this->cur_auto_order_index);
01849 if (in_list != NULL && this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID &&
01850 (!in_list->IsType(OT_AUTOMATIC) ||
01851 in_list->GetDestination() != this->last_station_visited) &&
01852 Order::CanAllocateItem()) {
01853 Order *auto_order = new Order();
01854 auto_order->MakeAutomatic(this->last_station_visited);
01855 InsertOrder(this, auto_order, this->cur_auto_order_index);
01856 if (this->cur_auto_order_index > 0) --this->cur_auto_order_index;
01857 }
01858 this->current_order.MakeLoading(false);
01859 }
01860
01861 Station::Get(this->last_station_visited)->loading_vehicles.push_back(this);
01862
01863 PrepareUnload(this);
01864
01865 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
01866 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01867 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01868 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
01869
01870 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
01871 this->cur_speed = 0;
01872 this->MarkDirty();
01873 }
01874
01879 void Vehicle::LeaveStation()
01880 {
01881 assert(this->current_order.IsType(OT_LOADING));
01882
01883 delete this->cargo_payment;
01884
01885
01886 if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
01887
01888 this->current_order.MakeLeaveStation();
01889 Station *st = Station::Get(this->last_station_visited);
01890 st->loading_vehicles.remove(this);
01891
01892 HideFillingPercent(&this->fill_percent_te_id);
01893
01894 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
01895
01896 if (IsTileType(this->tile, MP_STATION)) TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
01897
01898 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
01899 }
01900 }
01901
01902
01908 void Vehicle::HandleLoading(bool mode)
01909 {
01910 switch (this->current_order.GetType()) {
01911 case OT_LOADING: {
01912 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
01913
01914
01915 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
01916
01917 this->PlayLeaveStationSound();
01918
01919 this->LeaveStation();
01920
01921 break;
01922 }
01923
01924 case OT_DUMMY: break;
01925
01926 default: return;
01927 }
01928
01929 this->IncrementAutoOrderIndex();
01930 }
01931
01938 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
01939 {
01940 CommandCost ret = CheckOwnership(this->owner);
01941 if (ret.Failed()) return ret;
01942
01943 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
01944 if (this->IsStoppedInDepot()) return CMD_ERROR;
01945
01946 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
01947 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
01948 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
01949
01950
01951
01952 if (flags & DC_EXEC) {
01953 this->current_order.SetDepotOrderType(ODTF_MANUAL);
01954 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
01955 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01956 }
01957 return CommandCost();
01958 }
01959
01960 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR;
01961 if (flags & DC_EXEC) {
01962
01963
01964 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
01965
01966 this->current_order.MakeDummy();
01967 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01968 }
01969 return CommandCost();
01970 }
01971
01972 TileIndex location;
01973 DestinationID destination;
01974 bool reverse;
01975 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};
01976 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
01977
01978 if (flags & DC_EXEC) {
01979 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
01980
01981 this->dest_tile = location;
01982 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
01983 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
01984 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01985
01986
01987 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
01988
01989 if (this->type == VEH_AIRCRAFT) {
01990 Aircraft *a = Aircraft::From(this);
01991 if (a->state == FLYING && a->targetairport != destination) {
01992
01993 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
01994 AircraftNextAirportPos_and_Order(a);
01995 }
01996 }
01997 }
01998
01999 return CommandCost();
02000
02001 }
02002
02007 void Vehicle::UpdateVisualEffect(bool allow_power_change)
02008 {
02009 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02010 const Engine *e = Engine::Get(this->engine_type);
02011
02012
02013 byte visual_effect;
02014 switch (e->type) {
02015 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
02016 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
02017 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
02018 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
02019 }
02020
02021
02022 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
02023 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
02024
02025 if (callback != CALLBACK_FAILED) {
02026 callback = GB(callback, 0, 8);
02027
02028
02029 if (callback == VE_DEFAULT) {
02030 assert(HasBit(callback, VE_DISABLE_EFFECT));
02031 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
02032 }
02033 visual_effect = callback;
02034 }
02035 }
02036
02037
02038 if (visual_effect == VE_DEFAULT ||
02039 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
02040
02041
02042 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
02043 if (visual_effect == VE_DEFAULT) {
02044 visual_effect = 1 << VE_DISABLE_EFFECT;
02045 } else {
02046 SetBit(visual_effect, VE_DISABLE_EFFECT);
02047 }
02048 } else {
02049 if (visual_effect == VE_DEFAULT) {
02050
02051 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
02052 }
02053 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
02054 }
02055 }
02056
02057 this->vcache.cached_vis_effect = visual_effect;
02058
02059 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
02060 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02061 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
02062 }
02063 }
02064
02065 static const int8 _vehicle_smoke_pos[8] = {
02066 1, 1, 1, 0, -1, -1, -1, 0
02067 };
02068
02073 void Vehicle::ShowVisualEffect() const
02074 {
02075 assert(this->IsPrimaryVehicle());
02076 bool sound = false;
02077
02078
02079
02080
02081
02082
02083 if (_settings_game.vehicle.smoke_amount == 0 ||
02084 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
02085 this->cur_speed < 2) {
02086 return;
02087 }
02088 if (this->type == VEH_TRAIN) {
02089 const Train *t = Train::From(this);
02090
02091
02092
02093
02094 if (HasBit(t->flags, VRF_REVERSING) ||
02095 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
02096 t->cur_speed >= t->Train::GetCurrentMaxSpeed())) {
02097 return;
02098 }
02099 }
02100
02101 const Vehicle *v = this;
02102
02103 do {
02104 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
02105 byte effect_type = GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
02106 bool disable_effect = HasBit(v->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
02107
02108
02109
02110
02111
02112
02113
02114
02115 if (disable_effect ||
02116 v->vehstatus & VS_HIDDEN ||
02117 (MayHaveBridgeAbove(v->tile) && IsBridgeAbove(v->tile)) ||
02118 IsDepotTile(v->tile) ||
02119 IsTunnelTile(v->tile) ||
02120 (v->type == VEH_TRAIN &&
02121 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
02122 continue;
02123 }
02124
02125 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
02126 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
02127
02128 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
02129 x = -x;
02130 y = -y;
02131 }
02132
02133 switch (effect_type) {
02134 case VE_TYPE_STEAM:
02135
02136
02137
02138
02139
02140 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / this->vcache.cached_max_speed))) == 0) {
02141 CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
02142 sound = true;
02143 }
02144 break;
02145
02146 case VE_TYPE_DIESEL: {
02147
02148
02149
02150
02151
02152
02153
02154
02155
02156
02157
02158 int power_weight_effect = 0;
02159 if (v->type == VEH_TRAIN) {
02160 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
02161 }
02162 if (this->cur_speed < (this->vcache.cached_max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
02163 Chance16((64 - ((this->cur_speed << 5) / this->vcache.cached_max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
02164 CreateEffectVehicleRel(v, x, y, 10, EV_DIESEL_SMOKE);
02165 sound = true;
02166 }
02167 break;
02168 }
02169
02170 case VE_TYPE_ELECTRIC:
02171
02172
02173
02174
02175
02176
02177 if (GB(v->tick_counter, 0, 2) == 0 &&
02178 Chance16((6 - ((this->cur_speed << 2) / this->vcache.cached_max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
02179 CreateEffectVehicleRel(v, x, y, 10, EV_ELECTRIC_SPARK);
02180 sound = true;
02181 }
02182 break;
02183
02184 default:
02185 break;
02186 }
02187 } while ((v = v->Next()) != NULL);
02188
02189 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
02190 }
02191
02196 void Vehicle::SetNext(Vehicle *next)
02197 {
02198 assert(this != next);
02199
02200 if (this->next != NULL) {
02201
02202 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02203 v->first = this->next;
02204 }
02205 this->next->previous = NULL;
02206 }
02207
02208 this->next = next;
02209
02210 if (this->next != NULL) {
02211
02212 if (this->next->previous != NULL) this->next->previous->next = NULL;
02213 this->next->previous = this;
02214 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02215 v->first = this->first;
02216 }
02217 }
02218 }
02219
02225 void Vehicle::AddToShared(Vehicle *shared_chain)
02226 {
02227 assert(this->previous_shared == NULL && this->next_shared == NULL);
02228
02229 if (shared_chain->orders.list == NULL) {
02230 assert(shared_chain->previous_shared == NULL);
02231 assert(shared_chain->next_shared == NULL);
02232 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
02233 }
02234
02235 this->next_shared = shared_chain->next_shared;
02236 this->previous_shared = shared_chain;
02237
02238 shared_chain->next_shared = this;
02239
02240 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
02241
02242 shared_chain->orders.list->AddVehicle(this);
02243 }
02244
02248 void Vehicle::RemoveFromShared()
02249 {
02250
02251
02252 bool were_first = (this->FirstShared() == this);
02253 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
02254
02255 this->orders.list->RemoveVehicle(this);
02256
02257 if (!were_first) {
02258
02259 this->previous_shared->next_shared = this->NextShared();
02260 }
02261
02262 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
02263
02264
02265 if (this->orders.list->GetNumVehicles() == 1) {
02266
02267 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
02268 InvalidateVehicleOrder(this->FirstShared(), 0);
02269 } else if (were_first) {
02270
02271
02272 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
02273 }
02274
02275 this->next_shared = NULL;
02276 this->previous_shared = NULL;
02277 }
02278
02279 void VehiclesYearlyLoop()
02280 {
02281 Vehicle *v;
02282 FOR_ALL_VEHICLES(v) {
02283 if (v->IsPrimaryVehicle()) {
02284
02285 Money profit = v->GetDisplayProfitThisYear();
02286 if (v->age >= 730 && profit < 0) {
02287 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
02288 SetDParam(0, v->index);
02289 SetDParam(1, profit);
02290 AddVehicleNewsItem(
02291 STR_NEWS_VEHICLE_IS_UNPROFITABLE,
02292 NS_ADVICE,
02293 v->index
02294 );
02295 }
02296 AI::NewEvent(v->owner, new AIEventVehicleUnprofitable(v->index));
02297 }
02298
02299 v->profit_last_year = v->profit_this_year;
02300 v->profit_this_year = 0;
02301 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
02302 }
02303 }
02304 }
02305
02306
02316 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
02317 {
02318 const Engine *e = Engine::GetIfValid(engine_type);
02319 assert(e != NULL);
02320
02321 switch (e->type) {
02322 case VEH_TRAIN:
02323 return (st->facilities & FACIL_TRAIN) != 0;
02324
02325 case VEH_ROAD:
02326
02327
02328
02329 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
02330
02331 case VEH_SHIP:
02332 return (st->facilities & FACIL_DOCK) != 0;
02333
02334 case VEH_AIRCRAFT:
02335 return (st->facilities & FACIL_AIRPORT) != 0 &&
02336 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
02337
02338 default:
02339 return false;
02340 }
02341 }
02342
02349 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
02350 {
02351 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
02352
02353 return CanVehicleUseStation(v->engine_type, st);
02354 }
02355
02361 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
02362 {
02363 assert(this->IsGroundVehicle());
02364 if (this->type == VEH_TRAIN) {
02365 return &Train::From(this)->gcache;
02366 } else {
02367 return &RoadVehicle::From(this)->gcache;
02368 }
02369 }
02370
02376 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
02377 {
02378 assert(this->IsGroundVehicle());
02379 if (this->type == VEH_TRAIN) {
02380 return &Train::From(this)->gcache;
02381 } else {
02382 return &RoadVehicle::From(this)->gcache;
02383 }
02384 }
02385
02394 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
02395 {
02396 if (v->type == VEH_TRAIN) {
02397 Train *u = Train::From(v);
02398
02399 u = u->GetFirstEnginePart();
02400
02401
02402 for (; u != NULL && num_vehicles > 0; num_vehicles--) {
02403 do {
02404
02405 set.Include(u->index);
02406
02407
02408 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
02409
02410 u = u->Next();
02411 } while (u != NULL && u->IsArticulatedPart());
02412 }
02413 }
02414 }