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 "functions.h"
00033 #include "date_func.h"
00034 #include "window_func.h"
00035 #include "vehicle_func.h"
00036 #include "autoreplace_func.h"
00037 #include "autoreplace_gui.h"
00038 #include "station_base.h"
00039 #include "ai/ai.hpp"
00040 #include "depot_func.h"
00041 #include "network/network.h"
00042 #include "core/pool_func.hpp"
00043 #include "economy_base.h"
00044 #include "articulated_vehicles.h"
00045 #include "roadstop_base.h"
00046 #include "core/random_func.hpp"
00047 #include "core/backup_type.hpp"
00048 #include "order_backup.h"
00049 #include "sound_func.h"
00050 #include "effectvehicle_func.h"
00051 #include "effectvehicle_base.h"
00052 #include "vehiclelist.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
00902 AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
00903 v->x_extent, v->y_extent, v->z_extent, v->z_pos, (v->vehstatus & VS_SHADOW) != 0);
00904 }
00905
00910 void ViewportAddVehicles(DrawPixelInfo *dpi)
00911 {
00912
00913 const int l = dpi->left;
00914 const int r = dpi->left + dpi->width;
00915 const int t = dpi->top;
00916 const int b = dpi->top + dpi->height;
00917
00918
00919 int xl, xu, yl, yu;
00920
00921 if (dpi->width + 70 < (1 << (7 + 6))) {
00922 xl = GB(l - 70, 7, 6);
00923 xu = GB(r, 7, 6);
00924 } else {
00925
00926 xl = 0;
00927 xu = 0x3F;
00928 }
00929
00930 if (dpi->height + 70 < (1 << (6 + 6))) {
00931 yl = GB(t - 70, 6, 6) << 6;
00932 yu = GB(b, 6, 6) << 6;
00933 } else {
00934
00935 yl = 0;
00936 yu = 0x3F << 6;
00937 }
00938
00939 for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
00940 for (int x = xl;; x = (x + 1) & 0x3F) {
00941 const Vehicle *v = _vehicle_position_hash[x + y];
00942
00943 while (v != NULL) {
00944 if (!(v->vehstatus & VS_HIDDEN) &&
00945 l <= v->coord.right &&
00946 t <= v->coord.bottom &&
00947 r >= v->coord.left &&
00948 b >= v->coord.top) {
00949 DoDrawVehicle(v);
00950 }
00951 v = v->next_hash;
00952 }
00953
00954 if (x == xu) break;
00955 }
00956
00957 if (y == yu) break;
00958 }
00959 }
00960
00968 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
00969 {
00970 Vehicle *found = NULL, *v;
00971 uint dist, best_dist = UINT_MAX;
00972
00973 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
00974
00975 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
00976 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
00977
00978 FOR_ALL_VEHICLES(v) {
00979 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
00980 x >= v->coord.left && x <= v->coord.right &&
00981 y >= v->coord.top && y <= v->coord.bottom) {
00982
00983 dist = max(
00984 abs(((v->coord.left + v->coord.right) >> 1) - x),
00985 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
00986 );
00987
00988 if (dist < best_dist) {
00989 found = v;
00990 best_dist = dist;
00991 }
00992 }
00993 }
00994
00995 return found;
00996 }
00997
01002 void DecreaseVehicleValue(Vehicle *v)
01003 {
01004 v->value -= v->value >> 8;
01005 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01006 }
01007
01008 static const byte _breakdown_chance[64] = {
01009 3, 3, 3, 3, 3, 3, 3, 3,
01010 4, 4, 5, 5, 6, 6, 7, 7,
01011 8, 8, 9, 9, 10, 10, 11, 11,
01012 12, 13, 13, 13, 13, 14, 15, 16,
01013 17, 19, 21, 25, 28, 31, 34, 37,
01014 40, 44, 48, 52, 56, 60, 64, 68,
01015 72, 80, 90, 100, 110, 120, 130, 140,
01016 150, 170, 190, 210, 230, 250, 250, 250,
01017 };
01018
01019 void CheckVehicleBreakdown(Vehicle *v)
01020 {
01021 int rel, rel_old;
01022
01023
01024 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
01025 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01026
01027 if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
01028 _settings_game.difficulty.vehicle_breakdowns < 1 ||
01029 v->cur_speed < 5 || _game_mode == GM_MENU) {
01030 return;
01031 }
01032
01033 uint32 r = Random();
01034
01035
01036 int chance = v->breakdown_chance + 1;
01037 if (Chance16I(1, 25, r)) chance += 25;
01038 v->breakdown_chance = min(255, chance);
01039
01040
01041 rel = v->reliability;
01042 if (v->type == VEH_SHIP) rel += 0x6666;
01043
01044
01045 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
01046
01047
01048 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
01049 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
01050 v->breakdown_delay = GB(r, 24, 7) + 0x80;
01051 v->breakdown_chance = 0;
01052 }
01053 }
01054
01061 bool Vehicle::HandleBreakdown()
01062 {
01063
01064
01065
01066
01067
01068 switch (this->breakdown_ctr) {
01069 case 0:
01070 return false;
01071
01072 case 2:
01073 this->breakdown_ctr = 1;
01074
01075 if (this->breakdowns_since_last_service != 255) {
01076 this->breakdowns_since_last_service++;
01077 }
01078
01079 this->MarkDirty();
01080 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01081 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01082
01083 if (this->type == VEH_AIRCRAFT) {
01084
01085 this->vehstatus |= VS_AIRCRAFT_BROKEN;
01086 } else {
01087 this->cur_speed = 0;
01088
01089 if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
01090 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
01091 (this->type == VEH_TRAIN ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
01092 (this->type == VEH_TRAIN ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
01093 }
01094
01095 if (!(this->vehstatus & VS_HIDDEN)) {
01096 EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
01097 if (u != NULL) u->animation_state = this->breakdown_delay * 2;
01098 }
01099 }
01100
01101 case 1:
01102
01103 if (this->type == VEH_AIRCRAFT) return false;
01104
01105
01106 if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
01107 if (--this->breakdown_delay == 0) {
01108 this->breakdown_ctr = 0;
01109 this->MarkDirty();
01110 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01111 }
01112 }
01113 return true;
01114
01115 default:
01116 if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
01117 return false;
01118 }
01119 }
01120
01125 void AgeVehicle(Vehicle *v)
01126 {
01127 if (v->age < MAX_DAY) v->age++;
01128
01129 int age = v->age - v->max_age;
01130 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
01131 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
01132 v->reliability_spd_dec <<= 1;
01133 }
01134
01135 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01136
01137
01138 if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
01139
01140
01141 if (Company::Get(v->owner)->settings.engine_renew && Engine::Get(v->engine_type)->company_avail != 0) return;
01142
01143 StringID str;
01144 if (age == -DAYS_IN_LEAP_YEAR) {
01145 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
01146 } else if (age == 0) {
01147 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
01148 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
01149 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
01150 } else {
01151 return;
01152 }
01153
01154 SetDParam(0, v->index);
01155 AddVehicleNewsItem(str, NS_ADVICE, v->index);
01156 }
01157
01164 uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *colour)
01165 {
01166 int count = 0;
01167 int max = 0;
01168 int cars = 0;
01169 int unloading = 0;
01170 bool loading = false;
01171
01172 const Vehicle *u = v;
01173
01174 const Station *st = Station::GetIfValid(v->last_station_visited);
01175 assert(colour == NULL || st != NULL);
01176
01177
01178 for (; v != NULL; v = v->Next()) {
01179 count += v->cargo.Count();
01180 max += v->cargo_cap;
01181 if (v->cargo_cap != 0 && colour != NULL) {
01182 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
01183 loading |= !(u->current_order.GetLoadType() & OLFB_NO_LOAD) && st->goods[v->cargo_type].days_since_pickup != 255;
01184 cars++;
01185 }
01186 }
01187
01188 if (colour != NULL) {
01189 if (unloading == 0 && loading) {
01190 *colour = STR_PERCENT_UP;
01191 } else if (cars == unloading || !loading) {
01192 *colour = STR_PERCENT_DOWN;
01193 } else {
01194 *colour = STR_PERCENT_UP_DOWN;
01195 }
01196 }
01197
01198
01199 if (max == 0) return 100;
01200
01201
01202 return (count * 100) / max;
01203 }
01204
01209 void VehicleEnterDepot(Vehicle *v)
01210 {
01211
01212 assert(v == v->First());
01213
01214 switch (v->type) {
01215 case VEH_TRAIN: {
01216 Train *t = Train::From(v);
01217 SetWindowClassesDirty(WC_TRAINS_LIST);
01218
01219 SetDepotReservation(t->tile, false);
01220 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
01221
01222 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
01223 t->wait_counter = 0;
01224 t->force_proceed = TFP_NONE;
01225 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
01226 t->ConsistChanged(true);
01227 break;
01228 }
01229
01230 case VEH_ROAD:
01231 SetWindowClassesDirty(WC_ROADVEH_LIST);
01232 break;
01233
01234 case VEH_SHIP: {
01235 SetWindowClassesDirty(WC_SHIPS_LIST);
01236 Ship *ship = Ship::From(v);
01237 ship->state = TRACK_BIT_DEPOT;
01238 ship->UpdateCache();
01239 ship->UpdateViewport(true, true);
01240 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01241 break;
01242 }
01243
01244 case VEH_AIRCRAFT:
01245 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01246 HandleAircraftEnterHangar(Aircraft::From(v));
01247 break;
01248 default: NOT_REACHED();
01249 }
01250 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01251
01252 if (v->type != VEH_TRAIN) {
01253
01254
01255 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01256 }
01257 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01258
01259 v->vehstatus |= VS_HIDDEN;
01260 v->cur_speed = 0;
01261
01262 VehicleServiceInDepot(v);
01263
01264 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01265
01266 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01267 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01268
01269 const Order *real_order = v->GetOrder(v->cur_real_order_index);
01270 Order t = v->current_order;
01271 v->current_order.MakeDummy();
01272
01273
01274
01275 if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
01276 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
01277 (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
01278
01279 return;
01280 }
01281
01282 if (t.IsRefit()) {
01283 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01284 CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
01285 cur_company.Restore();
01286
01287 if (cost.Failed()) {
01288 _vehicles_to_autoreplace[v] = false;
01289 if (v->owner == _local_company) {
01290
01291 SetDParam(0, v->index);
01292 AddVehicleNewsItem(STR_NEWS_ORDER_REFIT_FAILED, NS_ADVICE, v->index);
01293 }
01294 } else if (cost.GetCost() != 0) {
01295 v->profit_this_year -= cost.GetCost() << 8;
01296 if (v->owner == _local_company) {
01297 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01298 }
01299 }
01300 }
01301
01302 if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01303
01304 v->DeleteUnreachedAutoOrders();
01305 UpdateVehicleTimetable(v, true);
01306 v->IncrementAutoOrderIndex();
01307 }
01308 if (t.GetDepotActionType() & ODATFB_HALT) {
01309
01310 _vehicles_to_autoreplace[v] = false;
01311 if (v->owner == _local_company) {
01312 SetDParam(0, v->index);
01313 AddVehicleNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, NS_ADVICE, v->index);
01314 }
01315 AI::NewEvent(v->owner, new AIEventVehicleWaitingInDepot(v->index));
01316 }
01317 }
01318 }
01319
01320
01328 void VehicleMove(Vehicle *v, bool update_viewport)
01329 {
01330 int img = v->cur_image;
01331 Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
01332 const Sprite *spr = GetSprite(img, ST_NORMAL);
01333
01334 pt.x += spr->x_offs;
01335 pt.y += spr->y_offs;
01336
01337 UpdateVehiclePosHash(v, pt.x, pt.y);
01338
01339 Rect old_coord = v->coord;
01340 v->coord.left = pt.x;
01341 v->coord.top = pt.y;
01342 v->coord.right = pt.x + spr->width + 2;
01343 v->coord.bottom = pt.y + spr->height + 2;
01344
01345 if (update_viewport) {
01346 MarkAllViewportsDirty(
01347 min(old_coord.left, v->coord.left),
01348 min(old_coord.top, v->coord.top),
01349 max(old_coord.right, v->coord.right) + 1,
01350 max(old_coord.bottom, v->coord.bottom) + 1
01351 );
01352 }
01353 }
01354
01363 void MarkSingleVehicleDirty(const Vehicle *v)
01364 {
01365 MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1, v->coord.bottom + 1);
01366 }
01367
01373 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01374 {
01375 static const int8 _delta_coord[16] = {
01376 -1,-1,-1, 0, 1, 1, 1, 0,
01377 -1, 0, 1, 1, 1, 0,-1,-1,
01378 };
01379
01380 int x = v->x_pos + _delta_coord[v->direction];
01381 int y = v->y_pos + _delta_coord[v->direction + 8];
01382
01383 GetNewVehiclePosResult gp;
01384 gp.x = x;
01385 gp.y = y;
01386 gp.old_tile = v->tile;
01387 gp.new_tile = TileVirtXY(x, y);
01388 return gp;
01389 }
01390
01391 static const Direction _new_direction_table[] = {
01392 DIR_N, DIR_NW, DIR_W,
01393 DIR_NE, DIR_SE, DIR_SW,
01394 DIR_E, DIR_SE, DIR_S
01395 };
01396
01397 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01398 {
01399 int i = 0;
01400
01401 if (y >= v->y_pos) {
01402 if (y != v->y_pos) i += 3;
01403 i += 3;
01404 }
01405
01406 if (x >= v->x_pos) {
01407 if (x != v->x_pos) i++;
01408 i++;
01409 }
01410
01411 Direction dir = v->direction;
01412
01413 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01414 if (dirdiff == DIRDIFF_SAME) return dir;
01415 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01416 }
01417
01427 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01428 {
01429 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01430 }
01431
01439 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01440 {
01441
01442 const Vehicle *v;
01443 FOR_ALL_VEHICLES(v) {
01444 if (v->type == type && v->owner == owner) {
01445 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01446 }
01447 }
01448
01449 if (this->maxid == 0) return;
01450
01451
01452
01453
01454 this->cache = CallocT<bool>(this->maxid + 2);
01455
01456
01457 FOR_ALL_VEHICLES(v) {
01458 if (v->type == type && v->owner == owner) {
01459 this->cache[v->unitnumber] = true;
01460 }
01461 }
01462 }
01463
01465 UnitID FreeUnitIDGenerator::NextID()
01466 {
01467 if (this->maxid <= this->curid) return ++this->curid;
01468
01469 while (this->cache[++this->curid]) { }
01470
01471 return this->curid;
01472 }
01473
01479 UnitID GetFreeUnitNumber(VehicleType type)
01480 {
01481
01482 uint max_veh;
01483 switch (type) {
01484 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
01485 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
01486 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
01487 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
01488 default: NOT_REACHED();
01489 }
01490
01491 uint amounts[4];
01492 CountCompanyVehicles(_current_company, amounts);
01493 assert((uint)type < lengthof(amounts));
01494 if (amounts[type] >= max_veh) return UINT16_MAX;
01495
01496 FreeUnitIDGenerator gen(type, _current_company);
01497
01498 return gen.NextID();
01499 }
01500
01501
01510 bool CanBuildVehicleInfrastructure(VehicleType type)
01511 {
01512 assert(IsCompanyBuildableVehicleType(type));
01513
01514 if (!Company::IsValidID(_local_company)) return false;
01515 if (!_settings_client.gui.disable_unsuitable_building) return true;
01516
01517 UnitID max;
01518 switch (type) {
01519 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
01520 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
01521 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
01522 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01523 default: NOT_REACHED();
01524 }
01525
01526
01527 if (max > 0) {
01528
01529 const Engine *e;
01530 FOR_ALL_ENGINES_OF_TYPE(e, type) {
01531 if (HasBit(e->company_avail, _local_company)) return true;
01532 }
01533 return false;
01534 }
01535
01536
01537 const Vehicle *v;
01538 FOR_ALL_VEHICLES(v) {
01539 if (v->owner == _local_company && v->type == type) return true;
01540 }
01541
01542 return false;
01543 }
01544
01545
01553 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
01554 {
01555 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01556 const Engine *e = Engine::Get(engine_type);
01557 switch (e->type) {
01558 default: NOT_REACHED();
01559 case VEH_TRAIN:
01560 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
01561
01562
01563 engine_type = parent_engine_type;
01564 e = Engine::Get(engine_type);
01565
01566 }
01567
01568 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01569 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01570 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
01571 if (!CargoSpec::Get(cargo_type)->is_freight) {
01572 if (parent_engine_type == INVALID_ENGINE) {
01573 return LS_PASSENGER_WAGON_STEAM;
01574 } else {
01575 switch (RailVehInfo(parent_engine_type)->engclass) {
01576 default: NOT_REACHED();
01577 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
01578 case EC_DIESEL: return LS_PASSENGER_WAGON_DIESEL;
01579 case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
01580 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
01581 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
01582 }
01583 }
01584 } else {
01585 return LS_FREIGHT_WAGON;
01586 }
01587 } else {
01588 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
01589
01590 switch (e->u.rail.engclass) {
01591 default: NOT_REACHED();
01592 case EC_STEAM: return LS_STEAM;
01593 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
01594 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
01595 case EC_MONORAIL: return LS_MONORAIL;
01596 case EC_MAGLEV: return LS_MAGLEV;
01597 }
01598 }
01599
01600 case VEH_ROAD:
01601
01602 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01603 engine_type = parent_engine_type;
01604 e = Engine::Get(engine_type);
01605 cargo_type = v->First()->cargo_type;
01606 }
01607 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01608 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01609
01610
01611 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
01612
01613 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01614 } else {
01615
01616 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01617 }
01618
01619 case VEH_SHIP:
01620 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01621 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01622 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01623
01624 case VEH_AIRCRAFT:
01625 switch (e->u.air.subtype) {
01626 case AIR_HELI: return LS_HELICOPTER;
01627 case AIR_CTOL: return LS_SMALL_PLANE;
01628 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
01629 default: NOT_REACHED();
01630 }
01631 }
01632 }
01633
01643 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
01644 {
01645 const Company *c = Company::Get(company);
01646 LiveryScheme scheme = LS_DEFAULT;
01647
01648
01649
01650 if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
01651
01652 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
01653
01654
01655 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01656 }
01657
01658 return &c->livery[scheme];
01659 }
01660
01661
01662 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01663 {
01664 PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01665
01666
01667 if (map != PAL_NONE) return map;
01668
01669 const Engine *e = Engine::Get(engine_type);
01670
01671
01672 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
01673 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01674
01675 if (callback != CALLBACK_FAILED) {
01676 assert_compile(PAL_NONE == 0);
01677 map = GB(callback, 0, 14);
01678
01679
01680 if (!HasBit(callback, 14)) {
01681
01682 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01683 return map;
01684 }
01685 }
01686 }
01687
01688 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
01689
01690 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
01691
01692
01693 if (!Company::IsValidID(company)) return map;
01694
01695 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
01696
01697 map += livery->colour1;
01698 if (twocc) map += livery->colour2 * 16;
01699
01700
01701 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01702 return map;
01703 }
01704
01711 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
01712 {
01713 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01714 }
01715
01721 PaletteID GetVehiclePalette(const Vehicle *v)
01722 {
01723 if (v->IsGroundVehicle()) {
01724 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
01725 }
01726
01727 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01728 }
01729
01738 uint GetVehicleCapacity(const Vehicle *v, uint16 *mail_capacity)
01739 {
01740 if (mail_capacity != NULL) *mail_capacity = 0;
01741 const Engine *e = Engine::Get(v->engine_type);
01742
01743 if (!e->CanCarryCargo()) return 0;
01744
01745 if (mail_capacity != NULL && e->type == VEH_AIRCRAFT && IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
01746 *mail_capacity = GetVehicleProperty(v, PROP_AIRCRAFT_MAIL_CAPACITY, e->u.air.mail_capacity);
01747 }
01748 CargoID default_cargo = e->GetDefaultCargoType();
01749
01750
01751
01752 if (HasBit(e->info.callback_mask, CBM_VEHICLE_REFIT_CAPACITY) &&
01753 (default_cargo != v->cargo_type || v->cargo_subtype != 0)) {
01754 uint16 callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
01755 if (callback != CALLBACK_FAILED) return callback;
01756 }
01757
01758
01759 uint capacity;
01760 switch (e->type) {
01761 case VEH_TRAIN: capacity = GetVehicleProperty(v, PROP_TRAIN_CARGO_CAPACITY, e->u.rail.capacity); break;
01762 case VEH_ROAD: capacity = GetVehicleProperty(v, PROP_ROADVEH_CARGO_CAPACITY, e->u.road.capacity); break;
01763 case VEH_SHIP: capacity = GetVehicleProperty(v, PROP_SHIP_CARGO_CAPACITY, e->u.ship.capacity); break;
01764 case VEH_AIRCRAFT: capacity = GetVehicleProperty(v, PROP_AIRCRAFT_PASSENGER_CAPACITY, e->u.air.passenger_capacity); break;
01765 default: NOT_REACHED();
01766 }
01767
01768
01769
01770 if (e->type != VEH_SHIP) {
01771 if (e->type == VEH_AIRCRAFT) {
01772 if (!IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
01773 capacity += GetVehicleProperty(v, PROP_AIRCRAFT_MAIL_CAPACITY, e->u.air.mail_capacity);
01774 }
01775 if (v->cargo_type == CT_MAIL) return capacity;
01776 } else {
01777 switch (default_cargo) {
01778 case CT_PASSENGERS: break;
01779 case CT_MAIL:
01780 case CT_GOODS: capacity *= 2; break;
01781 default: capacity *= 4; break;
01782 }
01783 }
01784 switch (v->cargo_type) {
01785 case CT_PASSENGERS: break;
01786 case CT_MAIL:
01787 case CT_GOODS: capacity /= 2; break;
01788 default: capacity /= 4; break;
01789 }
01790 }
01791
01792 return capacity;
01793 }
01794
01798 void Vehicle::DeleteUnreachedAutoOrders()
01799 {
01800 const Order *order = this->GetOrder(this->cur_auto_order_index);
01801 while (order != NULL) {
01802 if (this->cur_auto_order_index == this->cur_real_order_index) break;
01803
01804 if (order->IsType(OT_AUTOMATIC)) {
01805
01806 order = order->next;
01807 DeleteOrder(this, this->cur_auto_order_index);
01808 } else {
01809
01810 order = order->next;
01811 this->cur_auto_order_index++;
01812 }
01813
01814
01815 if (order == NULL) {
01816 order = this->GetOrder(0);
01817 this->cur_auto_order_index = 0;
01818 }
01819 }
01820 }
01821
01826 void Vehicle::BeginLoading()
01827 {
01828 assert(IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP);
01829
01830 if (this->current_order.IsType(OT_GOTO_STATION) &&
01831 this->current_order.GetDestination() == this->last_station_visited) {
01832 this->DeleteUnreachedAutoOrders();
01833
01834
01835 this->current_order.MakeLoading(true);
01836 UpdateVehicleTimetable(this, true);
01837
01838
01839
01840
01841
01842
01843 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
01844
01845 } else {
01846
01847
01848
01849 Order *in_list = this->GetOrder(this->cur_auto_order_index);
01850 if (in_list != NULL && this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID &&
01851 (!in_list->IsType(OT_AUTOMATIC) ||
01852 in_list->GetDestination() != this->last_station_visited)) {
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 if (disable_effect ||
02115 v->vehstatus & VS_HIDDEN ||
02116 IsDepotTile(v->tile) ||
02117 IsTunnelTile(v->tile) ||
02118 (v->type == VEH_TRAIN &&
02119 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
02120 continue;
02121 }
02122
02123 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
02124 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
02125
02126 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
02127 x = -x;
02128 y = -y;
02129 }
02130
02131 switch (effect_type) {
02132 case VE_TYPE_STEAM:
02133
02134
02135
02136
02137
02138 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / this->vcache.cached_max_speed))) == 0) {
02139 CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
02140 sound = true;
02141 }
02142 break;
02143
02144 case VE_TYPE_DIESEL: {
02145
02146
02147
02148
02149
02150
02151
02152
02153
02154
02155
02156 int power_weight_effect = 0;
02157 if (v->type == VEH_TRAIN) {
02158 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
02159 }
02160 if (this->cur_speed < (this->vcache.cached_max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
02161 Chance16((64 - ((this->cur_speed << 5) / this->vcache.cached_max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
02162 CreateEffectVehicleRel(v, x, y, 10, EV_DIESEL_SMOKE);
02163 sound = true;
02164 }
02165 break;
02166 }
02167
02168 case VE_TYPE_ELECTRIC:
02169
02170
02171
02172
02173
02174
02175 if (GB(v->tick_counter, 0, 2) == 0 &&
02176 Chance16((6 - ((this->cur_speed << 2) / this->vcache.cached_max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
02177 CreateEffectVehicleRel(v, x, y, 10, EV_ELECTRIC_SPARK);
02178 sound = true;
02179 }
02180 break;
02181
02182 default:
02183 break;
02184 }
02185 } while ((v = v->Next()) != NULL);
02186
02187 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
02188 }
02189
02194 void Vehicle::SetNext(Vehicle *next)
02195 {
02196 assert(this != next);
02197
02198 if (this->next != NULL) {
02199
02200 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02201 v->first = this->next;
02202 }
02203 this->next->previous = NULL;
02204 }
02205
02206 this->next = next;
02207
02208 if (this->next != NULL) {
02209
02210 if (this->next->previous != NULL) this->next->previous->next = NULL;
02211 this->next->previous = this;
02212 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02213 v->first = this->first;
02214 }
02215 }
02216 }
02217
02223 void Vehicle::AddToShared(Vehicle *shared_chain)
02224 {
02225 assert(this->previous_shared == NULL && this->next_shared == NULL);
02226
02227 if (!shared_chain->orders.list) {
02228 assert(shared_chain->previous_shared == NULL);
02229 assert(shared_chain->next_shared == NULL);
02230 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
02231 }
02232
02233 this->next_shared = shared_chain->next_shared;
02234 this->previous_shared = shared_chain;
02235
02236 shared_chain->next_shared = this;
02237
02238 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
02239
02240 shared_chain->orders.list->AddVehicle(this);
02241 }
02242
02246 void Vehicle::RemoveFromShared()
02247 {
02248
02249
02250 bool were_first = (this->FirstShared() == this);
02251 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
02252
02253 this->orders.list->RemoveVehicle(this);
02254
02255 if (!were_first) {
02256
02257 this->previous_shared->next_shared = this->NextShared();
02258 }
02259
02260 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
02261
02262
02263 if (this->orders.list->GetNumVehicles() == 1) {
02264
02265 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
02266 InvalidateVehicleOrder(this->FirstShared(), 0);
02267 } else if (were_first) {
02268
02269
02270 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
02271 }
02272
02273 this->next_shared = NULL;
02274 this->previous_shared = NULL;
02275 }
02276
02277 void VehiclesYearlyLoop()
02278 {
02279 Vehicle *v;
02280 FOR_ALL_VEHICLES(v) {
02281 if (v->IsPrimaryVehicle()) {
02282
02283 Money profit = v->GetDisplayProfitThisYear();
02284 if (v->age >= 730 && profit < 0) {
02285 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
02286 SetDParam(0, v->index);
02287 SetDParam(1, profit);
02288 AddVehicleNewsItem(
02289 STR_NEWS_VEHICLE_IS_UNPROFITABLE,
02290 NS_ADVICE,
02291 v->index
02292 );
02293 }
02294 AI::NewEvent(v->owner, new AIEventVehicleUnprofitable(v->index));
02295 }
02296
02297 v->profit_last_year = v->profit_this_year;
02298 v->profit_this_year = 0;
02299 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
02300 }
02301 }
02302 }
02303
02304
02314 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
02315 {
02316 const Engine *e = Engine::GetIfValid(engine_type);
02317 assert(e != NULL);
02318
02319 switch (e->type) {
02320 case VEH_TRAIN:
02321 return (st->facilities & FACIL_TRAIN) != 0;
02322
02323 case VEH_ROAD:
02324
02325
02326
02327 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
02328
02329 case VEH_SHIP:
02330 return (st->facilities & FACIL_DOCK) != 0;
02331
02332 case VEH_AIRCRAFT:
02333 return (st->facilities & FACIL_AIRPORT) != 0 &&
02334 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
02335
02336 default:
02337 return false;
02338 }
02339 }
02340
02347 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
02348 {
02349 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
02350
02351 return CanVehicleUseStation(v->engine_type, st);
02352 }
02353
02359 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
02360 {
02361 assert(this->IsGroundVehicle());
02362 if (this->type == VEH_TRAIN) {
02363 return &Train::From(this)->gcache;
02364 } else {
02365 return &RoadVehicle::From(this)->gcache;
02366 }
02367 }
02368
02374 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
02375 {
02376 assert(this->IsGroundVehicle());
02377 if (this->type == VEH_TRAIN) {
02378 return &Train::From(this)->gcache;
02379 } else {
02380 return &RoadVehicle::From(this)->gcache;
02381 }
02382 }
02383
02392 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
02393 {
02394 if (v->type == VEH_TRAIN) {
02395 Train *u = Train::From(v);
02396
02397 u = u->GetFirstEnginePart();
02398
02399
02400 for (; u != NULL && num_vehicles > 0; num_vehicles--) {
02401 do {
02402
02403 set.Include(u->index);
02404
02405
02406 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
02407
02408 u = u->Next();
02409 } while (u != NULL && u->IsArticulatedPart());
02410 }
02411 }
02412 }