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
00066
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 (_settings_game.order.gotodepot && 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 !Train::From(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
00909 void ViewportAddVehicles(DrawPixelInfo *dpi)
00910 {
00911
00912 const int l = dpi->left;
00913 const int r = dpi->left + dpi->width;
00914 const int t = dpi->top;
00915 const int b = dpi->top + dpi->height;
00916
00917
00918 int xl, xu, yl, yu;
00919
00920 if (dpi->width + 70 < (1 << (7 + 6))) {
00921 xl = GB(l - 70, 7, 6);
00922 xu = GB(r, 7, 6);
00923 } else {
00924
00925 xl = 0;
00926 xu = 0x3F;
00927 }
00928
00929 if (dpi->height + 70 < (1 << (6 + 6))) {
00930 yl = GB(t - 70, 6, 6) << 6;
00931 yu = GB(b, 6, 6) << 6;
00932 } else {
00933
00934 yl = 0;
00935 yu = 0x3F << 6;
00936 }
00937
00938 for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
00939 for (int x = xl;; x = (x + 1) & 0x3F) {
00940 const Vehicle *v = _vehicle_position_hash[x + y];
00941
00942 while (v != NULL) {
00943 if (!(v->vehstatus & VS_HIDDEN) ) {
00944 DoDrawVehicle(v);
00945 }
00946 v = v->next_hash;
00947 }
00948
00949 if (x == xu) break;
00950 }
00951
00952 if (y == yu) break;
00953 }
00954 }
00955
00956 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
00957 {
00958 Vehicle *found = NULL, *v;
00959 uint dist, best_dist = UINT_MAX;
00960
00961 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
00962
00963 x = ScaleByZoom(x + vp->virtual_left, vp->zoom);
00964 y = ScaleByZoom(y + vp->virtual_top, vp->zoom) ;
00965
00966 FOR_ALL_VEHICLES(v) {
00967 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
00968 x >= v->coord.left && x <= v->coord.right &&
00969 y >= v->coord.top && y <= v->coord.bottom) {
00970
00971 dist = max(
00972 abs(((v->coord.left + v->coord.right) >> 1) - x),
00973 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
00974 );
00975
00976 if (dist < best_dist) {
00977 found = v;
00978 best_dist = dist;
00979 }
00980 }
00981 }
00982
00983 return found;
00984 }
00985
00986 void DecreaseVehicleValue(Vehicle *v)
00987 {
00988 v->value -= v->value >> 8;
00989 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
00990 }
00991
00992 static const byte _breakdown_chance[64] = {
00993 3, 3, 3, 3, 3, 3, 3, 3,
00994 4, 4, 5, 5, 6, 6, 7, 7,
00995 8, 8, 9, 9, 10, 10, 11, 11,
00996 12, 13, 13, 13, 13, 14, 15, 16,
00997 17, 19, 21, 25, 28, 31, 34, 37,
00998 40, 44, 48, 52, 56, 60, 64, 68,
00999 72, 80, 90, 100, 110, 120, 130, 140,
01000 150, 170, 190, 210, 230, 250, 250, 250,
01001 };
01002
01003 void CheckVehicleBreakdown(Vehicle *v)
01004 {
01005 int rel, rel_old;
01006
01007
01008 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
01009 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01010
01011 if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
01012 _settings_game.difficulty.vehicle_breakdowns < 1 ||
01013 v->cur_speed < 5 || _game_mode == GM_MENU) {
01014 return;
01015 }
01016
01017 uint32 r = Random();
01018
01019
01020 int chance = v->breakdown_chance + 1;
01021 if (Chance16I(1, 25, r)) chance += 25;
01022 v->breakdown_chance = min(255, chance);
01023
01024
01025 rel = v->reliability;
01026 if (v->type == VEH_SHIP) rel += 0x6666;
01027
01028
01029 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
01030
01031
01032 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
01033 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
01034 v->breakdown_delay = GB(r, 24, 7) + 0x80;
01035 v->breakdown_chance = 0;
01036 }
01037 }
01038
01045 bool Vehicle::HandleBreakdown()
01046 {
01047
01048
01049
01050
01051
01052 switch (this->breakdown_ctr) {
01053 case 0:
01054 return false;
01055
01056 case 2:
01057 this->breakdown_ctr = 1;
01058
01059 if (this->breakdowns_since_last_service != 255) {
01060 this->breakdowns_since_last_service++;
01061 }
01062
01063 this->MarkDirty();
01064 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01065 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01066
01067 if (this->type == VEH_AIRCRAFT) {
01068
01069 this->vehstatus |= VS_AIRCRAFT_BROKEN;
01070 } else {
01071 this->cur_speed = 0;
01072
01073 if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
01074 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
01075 (this->type == VEH_TRAIN ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
01076 (this->type == VEH_TRAIN ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
01077 }
01078
01079 if (!(this->vehstatus & VS_HIDDEN)) {
01080 EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
01081 if (u != NULL) u->animation_state = this->breakdown_delay * 2;
01082 }
01083 }
01084
01085 case 1:
01086
01087 if (this->type == VEH_AIRCRAFT) return false;
01088
01089
01090 if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
01091 if (--this->breakdown_delay == 0) {
01092 this->breakdown_ctr = 0;
01093 this->MarkDirty();
01094 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01095 }
01096 }
01097 return true;
01098
01099 default:
01100 if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
01101 return false;
01102 }
01103 }
01104
01109 void AgeVehicle(Vehicle *v)
01110 {
01111 if (v->age < MAX_DAY) v->age++;
01112
01113 int age = v->age - v->max_age;
01114 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
01115 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
01116 v->reliability_spd_dec <<= 1;
01117 }
01118
01119 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01120
01121
01122 if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
01123
01124
01125 if (Company::Get(v->owner)->settings.engine_renew && Engine::Get(v->engine_type)->company_avail != 0) return;
01126
01127 StringID str;
01128 if (age == -DAYS_IN_LEAP_YEAR) {
01129 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
01130 } else if (age == 0) {
01131 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
01132 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
01133 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
01134 } else {
01135 return;
01136 }
01137
01138 SetDParam(0, v->index);
01139 AddVehicleNewsItem(str, NS_ADVICE, v->index);
01140 }
01141
01148 uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *colour)
01149 {
01150 int count = 0;
01151 int max = 0;
01152 int cars = 0;
01153 int unloading = 0;
01154 bool loading = false;
01155
01156 const Vehicle *u = v;
01157
01158 const Station *st = Station::GetIfValid(v->last_station_visited);
01159 assert(colour == NULL || st != NULL);
01160
01161
01162 for (; v != NULL; v = v->Next()) {
01163 count += v->cargo.Count();
01164 max += v->cargo_cap;
01165 if (v->cargo_cap != 0 && colour != NULL) {
01166 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
01167 loading |= !(u->current_order.GetLoadType() & OLFB_NO_LOAD) && st->goods[v->cargo_type].days_since_pickup != 255;
01168 cars++;
01169 }
01170 }
01171
01172 if (colour != NULL) {
01173 if (unloading == 0 && loading) {
01174 *colour = STR_PERCENT_UP;
01175 } else if (cars == unloading || !loading) {
01176 *colour = STR_PERCENT_DOWN;
01177 } else {
01178 *colour = STR_PERCENT_UP_DOWN;
01179 }
01180 }
01181
01182
01183 if (max == 0) return 100;
01184
01185
01186 return (count * 100) / max;
01187 }
01188
01189 void VehicleEnterDepot(Vehicle *v)
01190 {
01191
01192 assert(v == v->First());
01193
01194 switch (v->type) {
01195 case VEH_TRAIN: {
01196 Train *t = Train::From(v);
01197 SetWindowClassesDirty(WC_TRAINS_LIST);
01198
01199 SetDepotReservation(t->tile, false);
01200 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
01201
01202 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
01203 t->wait_counter = 0;
01204 t->force_proceed = TFP_NONE;
01205 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
01206 t->ConsistChanged(true);
01207 break;
01208 }
01209
01210 case VEH_ROAD:
01211 SetWindowClassesDirty(WC_ROADVEH_LIST);
01212 break;
01213
01214 case VEH_SHIP: {
01215 SetWindowClassesDirty(WC_SHIPS_LIST);
01216 Ship *ship = Ship::From(v);
01217 ship->state = TRACK_BIT_DEPOT;
01218 ship->UpdateCache();
01219 ship->UpdateViewport(true, true);
01220 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01221 break;
01222 }
01223
01224 case VEH_AIRCRAFT:
01225 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01226 HandleAircraftEnterHangar(Aircraft::From(v));
01227 break;
01228 default: NOT_REACHED();
01229 }
01230 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01231
01232 if (v->type != VEH_TRAIN) {
01233
01234
01235 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01236 }
01237 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01238
01239 v->vehstatus |= VS_HIDDEN;
01240 v->cur_speed = 0;
01241
01242 VehicleServiceInDepot(v);
01243
01244 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01245
01246 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01247 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01248
01249 const Order *real_order = v->GetNextManualOrder(v->cur_order_index);
01250 Order t = v->current_order;
01251 v->current_order.MakeDummy();
01252
01253
01254
01255 if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
01256 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
01257 (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
01258
01259 return;
01260 }
01261
01262 if (t.IsRefit()) {
01263 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01264 CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
01265 cur_company.Restore();
01266
01267 if (cost.Failed()) {
01268 _vehicles_to_autoreplace[v] = false;
01269 if (v->owner == _local_company) {
01270
01271 SetDParam(0, v->index);
01272 AddVehicleNewsItem(STR_NEWS_ORDER_REFIT_FAILED, NS_ADVICE, v->index);
01273 }
01274 } else if (cost.GetCost() != 0) {
01275 v->profit_this_year -= cost.GetCost() << 8;
01276 if (v->owner == _local_company) {
01277 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01278 }
01279 }
01280 }
01281
01282 if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01283
01284 v->DeleteUnreachedAutoOrders();
01285 UpdateVehicleTimetable(v, true);
01286 v->IncrementOrderIndex();
01287 }
01288 if (t.GetDepotActionType() & ODATFB_HALT) {
01289
01290 _vehicles_to_autoreplace[v] = false;
01291 if (v->owner == _local_company) {
01292 SetDParam(0, v->index);
01293 AddVehicleNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, NS_ADVICE, v->index);
01294 }
01295 AI::NewEvent(v->owner, new AIEventVehicleWaitingInDepot(v->index));
01296 }
01297 }
01298 }
01299
01300
01308 void VehicleMove(Vehicle *v, bool update_viewport)
01309 {
01310 int img = v->cur_image;
01311 Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
01312 const Sprite *spr = GetSprite(img, ST_NORMAL);
01313
01314 pt.x += spr->x_offs;
01315 pt.y += spr->y_offs;
01316
01317 UpdateVehiclePosHash(v, pt.x, pt.y);
01318
01319 Rect old_coord = v->coord;
01320 v->coord.left = pt.x;
01321 v->coord.top = pt.y;
01322 v->coord.right = pt.x + spr->width + 2;
01323 v->coord.bottom = pt.y + spr->height + 2;
01324
01325 if (update_viewport) {
01326 MarkAllViewportsDirty(
01327 min(old_coord.left, v->coord.left),
01328 min(old_coord.top, v->coord.top),
01329 max(old_coord.right, v->coord.right) + 1,
01330 max(old_coord.bottom, v->coord.bottom) + 1
01331 );
01332 }
01333 }
01334
01343 void MarkSingleVehicleDirty(const Vehicle *v)
01344 {
01345 MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1, v->coord.bottom + 1);
01346 }
01347
01353 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01354 {
01355 static const int8 _delta_coord[16] = {
01356 -1,-1,-1, 0, 1, 1, 1, 0,
01357 -1, 0, 1, 1, 1, 0,-1,-1,
01358 };
01359
01360 int x = v->x_pos + _delta_coord[v->direction];
01361 int y = v->y_pos + _delta_coord[v->direction + 8];
01362
01363 GetNewVehiclePosResult gp;
01364 gp.x = x;
01365 gp.y = y;
01366 gp.old_tile = v->tile;
01367 gp.new_tile = TileVirtXY(x, y);
01368 return gp;
01369 }
01370
01371 static const Direction _new_direction_table[] = {
01372 DIR_N, DIR_NW, DIR_W,
01373 DIR_NE, DIR_SE, DIR_SW,
01374 DIR_E, DIR_SE, DIR_S
01375 };
01376
01377 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01378 {
01379 int i = 0;
01380
01381 if (y >= v->y_pos) {
01382 if (y != v->y_pos) i += 3;
01383 i += 3;
01384 }
01385
01386 if (x >= v->x_pos) {
01387 if (x != v->x_pos) i++;
01388 i++;
01389 }
01390
01391 Direction dir = v->direction;
01392
01393 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01394 if (dirdiff == DIRDIFF_SAME) return dir;
01395 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01396 }
01397
01407 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01408 {
01409 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01410 }
01411
01419 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01420 {
01421
01422 const Vehicle *v;
01423 FOR_ALL_VEHICLES(v) {
01424 if (v->type == type && v->owner == owner) {
01425 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01426 }
01427 }
01428
01429 if (this->maxid == 0) return;
01430
01431
01432
01433
01434 this->cache = CallocT<bool>(this->maxid + 2);
01435
01436
01437 FOR_ALL_VEHICLES(v) {
01438 if (v->type == type && v->owner == owner) {
01439 this->cache[v->unitnumber] = true;
01440 }
01441 }
01442 }
01443
01445 UnitID FreeUnitIDGenerator::NextID()
01446 {
01447 if (this->maxid <= this->curid) return ++this->curid;
01448
01449 while (this->cache[++this->curid]) { }
01450
01451 return this->curid;
01452 }
01453
01459 UnitID GetFreeUnitNumber(VehicleType type)
01460 {
01461
01462 uint max_veh;
01463 switch (type) {
01464 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
01465 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
01466 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
01467 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
01468 default: NOT_REACHED();
01469 }
01470
01471 uint amounts[4];
01472 CountCompanyVehicles(_current_company, amounts);
01473 assert((uint)type < lengthof(amounts));
01474 if (amounts[type] >= max_veh) return UINT16_MAX;
01475
01476 FreeUnitIDGenerator gen(type, _current_company);
01477
01478 return gen.NextID();
01479 }
01480
01481
01490 bool CanBuildVehicleInfrastructure(VehicleType type)
01491 {
01492 assert(IsCompanyBuildableVehicleType(type));
01493
01494 if (!Company::IsValidID(_local_company)) return false;
01495 if (!_settings_client.gui.disable_unsuitable_building) return true;
01496
01497 UnitID max;
01498 switch (type) {
01499 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
01500 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
01501 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
01502 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01503 default: NOT_REACHED();
01504 }
01505
01506
01507 if (max > 0) {
01508
01509 const Engine *e;
01510 FOR_ALL_ENGINES_OF_TYPE(e, type) {
01511 if (HasBit(e->company_avail, _local_company)) return true;
01512 }
01513 return false;
01514 }
01515
01516
01517 const Vehicle *v;
01518 FOR_ALL_VEHICLES(v) {
01519 if (v->owner == _local_company && v->type == type) return true;
01520 }
01521
01522 return false;
01523 }
01524
01525
01533 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
01534 {
01535 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01536 const Engine *e = Engine::Get(engine_type);
01537 switch (e->type) {
01538 default: NOT_REACHED();
01539 case VEH_TRAIN:
01540 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (Train::From(v)->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
01541
01542
01543 engine_type = parent_engine_type;
01544 e = Engine::Get(engine_type);
01545
01546 }
01547
01548 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01549 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01550 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
01551 if (!CargoSpec::Get(cargo_type)->is_freight) {
01552 if (parent_engine_type == INVALID_ENGINE) {
01553 return LS_PASSENGER_WAGON_STEAM;
01554 } else {
01555 switch (RailVehInfo(parent_engine_type)->engclass) {
01556 default: NOT_REACHED();
01557 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
01558 case EC_DIESEL: return LS_PASSENGER_WAGON_DIESEL;
01559 case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
01560 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
01561 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
01562 }
01563 }
01564 } else {
01565 return LS_FREIGHT_WAGON;
01566 }
01567 } else {
01568 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
01569
01570 switch (e->u.rail.engclass) {
01571 default: NOT_REACHED();
01572 case EC_STEAM: return LS_STEAM;
01573 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
01574 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
01575 case EC_MONORAIL: return LS_MONORAIL;
01576 case EC_MAGLEV: return LS_MAGLEV;
01577 }
01578 }
01579
01580 case VEH_ROAD:
01581
01582 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01583 engine_type = parent_engine_type;
01584 e = Engine::Get(engine_type);
01585 cargo_type = v->First()->cargo_type;
01586 }
01587 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01588 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01589
01590
01591 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
01592
01593 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01594 } else {
01595
01596 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01597 }
01598
01599 case VEH_SHIP:
01600 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01601 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01602 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01603
01604 case VEH_AIRCRAFT:
01605 switch (e->u.air.subtype) {
01606 case AIR_HELI: return LS_HELICOPTER;
01607 case AIR_CTOL: return LS_SMALL_PLANE;
01608 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
01609 default: NOT_REACHED();
01610 }
01611 }
01612 }
01613
01623 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
01624 {
01625 const Company *c = Company::Get(company);
01626 LiveryScheme scheme = LS_DEFAULT;
01627
01628
01629
01630 if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
01631
01632 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
01633
01634
01635 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01636 }
01637
01638 return &c->livery[scheme];
01639 }
01640
01641
01642 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01643 {
01644 PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01645
01646
01647 if (map != PAL_NONE) return map;
01648
01649 const Engine *e = Engine::Get(engine_type);
01650
01651
01652 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
01653 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01654
01655 if (callback != CALLBACK_FAILED) {
01656 assert_compile(PAL_NONE == 0);
01657 map = GB(callback, 0, 14);
01658
01659
01660 if (!HasBit(callback, 14)) {
01661
01662 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01663 return map;
01664 }
01665 }
01666 }
01667
01668 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
01669
01670 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
01671
01672
01673 if (!Company::IsValidID(company)) return map;
01674
01675 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
01676
01677 map += livery->colour1;
01678 if (twocc) map += livery->colour2 * 16;
01679
01680
01681 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01682 return map;
01683 }
01684
01691 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
01692 {
01693 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01694 }
01695
01701 PaletteID GetVehiclePalette(const Vehicle *v)
01702 {
01703 if (v->IsGroundVehicle()) {
01704 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
01705 }
01706
01707 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01708 }
01709
01718 uint GetVehicleCapacity(const Vehicle *v, uint16 *mail_capacity)
01719 {
01720 if (mail_capacity != NULL) *mail_capacity = 0;
01721 const Engine *e = Engine::Get(v->engine_type);
01722
01723 if (!e->CanCarryCargo()) return 0;
01724
01725 if (mail_capacity != NULL && e->type == VEH_AIRCRAFT && IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
01726 *mail_capacity = GetVehicleProperty(v, PROP_AIRCRAFT_MAIL_CAPACITY, e->u.air.mail_capacity);
01727 }
01728 CargoID default_cargo = e->GetDefaultCargoType();
01729
01730
01731
01732 if (HasBit(e->info.callback_mask, CBM_VEHICLE_REFIT_CAPACITY) &&
01733 (default_cargo != v->cargo_type || v->cargo_subtype != 0)) {
01734 uint16 callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
01735 if (callback != CALLBACK_FAILED) return callback;
01736 }
01737
01738
01739 uint capacity;
01740 switch (e->type) {
01741 case VEH_TRAIN: capacity = GetVehicleProperty(v, PROP_TRAIN_CARGO_CAPACITY, e->u.rail.capacity); break;
01742 case VEH_ROAD: capacity = GetVehicleProperty(v, PROP_ROADVEH_CARGO_CAPACITY, e->u.road.capacity); break;
01743 case VEH_SHIP: capacity = GetVehicleProperty(v, PROP_SHIP_CARGO_CAPACITY, e->u.ship.capacity); break;
01744 case VEH_AIRCRAFT: capacity = GetVehicleProperty(v, PROP_AIRCRAFT_PASSENGER_CAPACITY, e->u.air.passenger_capacity); break;
01745 default: NOT_REACHED();
01746 }
01747
01748
01749
01750 if (e->type != VEH_SHIP) {
01751 if (e->type == VEH_AIRCRAFT) {
01752 if (!IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
01753 capacity += GetVehicleProperty(v, PROP_AIRCRAFT_MAIL_CAPACITY, e->u.air.mail_capacity);
01754 }
01755 if (v->cargo_type == CT_MAIL) return capacity;
01756 } else {
01757 switch (default_cargo) {
01758 case CT_PASSENGERS: break;
01759 case CT_MAIL:
01760 case CT_GOODS: capacity *= 2; break;
01761 default: capacity *= 4; break;
01762 }
01763 }
01764 switch (v->cargo_type) {
01765 case CT_PASSENGERS: break;
01766 case CT_MAIL:
01767 case CT_GOODS: capacity /= 2; break;
01768 default: capacity /= 4; break;
01769 }
01770 }
01771
01772 return capacity;
01773 }
01774
01778 void Vehicle::DeleteUnreachedAutoOrders()
01779 {
01780 const Order *order = this->GetOrder(this->cur_order_index);
01781 while (order != NULL && order->IsType(OT_AUTOMATIC)) {
01782
01783 order = order->next;
01784 DeleteOrder(this, this->cur_order_index);
01785 }
01786 }
01787
01788 void Vehicle::BeginLoading()
01789 {
01790 assert(IsTileType(tile, MP_STATION) || type == VEH_SHIP);
01791
01792 if (this->current_order.IsType(OT_GOTO_STATION) &&
01793 this->current_order.GetDestination() == this->last_station_visited) {
01794 this->DeleteUnreachedAutoOrders();
01795
01796
01797 this->current_order.MakeLoading(true);
01798 UpdateVehicleTimetable(this, true);
01799
01800
01801
01802
01803
01804
01805 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
01806
01807 } else {
01808
01809
01810
01811 Order *in_list = this->GetOrder(this->cur_order_index);
01812 if (in_list != NULL && this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID &&
01813 (!in_list->IsType(OT_AUTOMATIC) ||
01814 in_list->GetDestination() != this->last_station_visited)) {
01815 Order *auto_order = new Order();
01816 auto_order->MakeAutomatic(this->last_station_visited);
01817 InsertOrder(this, auto_order, this->cur_order_index);
01818 if (this->cur_order_index > 0) --this->cur_order_index;
01819 }
01820 this->current_order.MakeLoading(false);
01821 }
01822
01823 Station::Get(this->last_station_visited)->loading_vehicles.push_back(this);
01824
01825 PrepareUnload(this);
01826
01827 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
01828 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01829 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01830 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
01831
01832 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
01833 this->cur_speed = 0;
01834 this->MarkDirty();
01835 }
01836
01837 void Vehicle::LeaveStation()
01838 {
01839 assert(current_order.IsType(OT_LOADING));
01840
01841 delete this->cargo_payment;
01842
01843
01844 if (current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
01845
01846 current_order.MakeLeaveStation();
01847 Station *st = Station::Get(this->last_station_visited);
01848 st->loading_vehicles.remove(this);
01849
01850 HideFillingPercent(&this->fill_percent_te_id);
01851
01852 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
01853
01854 if (IsTileType(this->tile, MP_STATION)) TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
01855
01856 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
01857 }
01858 }
01859
01860
01866 void Vehicle::HandleLoading(bool mode)
01867 {
01868 switch (this->current_order.GetType()) {
01869 case OT_LOADING: {
01870 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
01871
01872
01873 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) ||
01874 (_settings_game.order.timetabling && this->current_order_time < wait_time)) return;
01875
01876 this->PlayLeaveStationSound();
01877
01878 this->LeaveStation();
01879
01880 break;
01881 }
01882
01883 case OT_DUMMY: break;
01884
01885 default: return;
01886 }
01887
01888 this->IncrementOrderIndex();
01889 }
01890
01897 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
01898 {
01899 CommandCost ret = CheckOwnership(this->owner);
01900 if (ret.Failed()) return ret;
01901
01902 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
01903 if (this->IsStoppedInDepot()) return CMD_ERROR;
01904
01905 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
01906 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
01907 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
01908
01909
01910
01911 if (flags & DC_EXEC) {
01912 this->current_order.SetDepotOrderType(ODTF_MANUAL);
01913 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
01914 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01915 }
01916 return CommandCost();
01917 }
01918
01919 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR;
01920 if (flags & DC_EXEC) {
01921
01922
01923 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementOrderIndex();
01924
01925 this->current_order.MakeDummy();
01926 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01927 }
01928 return CommandCost();
01929 }
01930
01931 TileIndex location;
01932 DestinationID destination;
01933 bool reverse;
01934 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};
01935 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
01936
01937 if (flags & DC_EXEC) {
01938 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
01939
01940 this->dest_tile = location;
01941 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
01942 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
01943 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01944
01945
01946 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
01947
01948 if (this->type == VEH_AIRCRAFT) {
01949 Aircraft *a = Aircraft::From(this);
01950 if (a->state == FLYING && a->targetairport != destination) {
01951
01952 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
01953 AircraftNextAirportPos_and_Order(a);
01954 }
01955 }
01956 }
01957
01958 return CommandCost();
01959
01960 }
01961
01966 void Vehicle::UpdateVisualEffect(bool allow_power_change)
01967 {
01968 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
01969 const Engine *e = Engine::Get(this->engine_type);
01970
01971
01972 byte visual_effect;
01973 switch (e->type) {
01974 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
01975 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
01976 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
01977 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
01978 }
01979
01980
01981 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
01982 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
01983
01984 if (callback != CALLBACK_FAILED) {
01985 callback = GB(callback, 0, 8);
01986
01987
01988 if (callback == VE_DEFAULT) {
01989 assert(HasBit(callback, VE_DISABLE_EFFECT));
01990 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
01991 }
01992 visual_effect = callback;
01993 }
01994 }
01995
01996
01997 if (visual_effect == VE_DEFAULT ||
01998 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
01999
02000
02001 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
02002 if (visual_effect == VE_DEFAULT) {
02003 visual_effect = 1 << VE_DISABLE_EFFECT;
02004 } else {
02005 SetBit(visual_effect, VE_DISABLE_EFFECT);
02006 }
02007 } else {
02008 if (visual_effect == VE_DEFAULT) {
02009
02010 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
02011 }
02012 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
02013 }
02014 }
02015
02016 this->vcache.cached_vis_effect = visual_effect;
02017
02018 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
02019 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02020 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
02021 }
02022 }
02023
02024 static const int8 _vehicle_smoke_pos[8] = {
02025 1, 1, 1, 0, -1, -1, -1, 0
02026 };
02027
02032 void Vehicle::ShowVisualEffect() const
02033 {
02034 assert(this->IsPrimaryVehicle());
02035 bool sound = false;
02036
02037
02038
02039
02040
02041
02042 if (_settings_game.vehicle.smoke_amount == 0 ||
02043 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
02044 this->cur_speed < 2) {
02045 return;
02046 }
02047 if (this->type == VEH_TRAIN) {
02048 const Train *t = Train::From(this);
02049
02050
02051
02052
02053 if (HasBit(t->flags, VRF_REVERSING) ||
02054 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
02055 t->cur_speed >= t->Train::GetCurrentMaxSpeed())) {
02056 return;
02057 }
02058 }
02059
02060 const Vehicle *v = this;
02061
02062 do {
02063 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
02064 byte effect_type = GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
02065 bool disable_effect = HasBit(v->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
02066
02067
02068
02069
02070
02071
02072
02073 if (disable_effect ||
02074 v->vehstatus & VS_HIDDEN ||
02075 IsDepotTile(v->tile) ||
02076 IsTunnelTile(v->tile) ||
02077 (v->type == VEH_TRAIN &&
02078 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
02079 continue;
02080 }
02081
02082 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
02083 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
02084
02085 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
02086 x = -x;
02087 y = -y;
02088 }
02089
02090 switch (effect_type) {
02091 case VE_TYPE_STEAM:
02092
02093
02094
02095
02096
02097 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / this->vcache.cached_max_speed))) == 0) {
02098 CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
02099 sound = true;
02100 }
02101 break;
02102
02103 case VE_TYPE_DIESEL: {
02104
02105
02106
02107
02108
02109
02110
02111
02112
02113
02114
02115 int power_weight_effect = 0;
02116 if (v->type == VEH_TRAIN) {
02117 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
02118 }
02119 if (this->cur_speed < (this->vcache.cached_max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
02120 Chance16((64 - ((this->cur_speed << 5) / this->vcache.cached_max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
02121 CreateEffectVehicleRel(v, x, y, 10, EV_DIESEL_SMOKE);
02122 sound = true;
02123 }
02124 break;
02125 }
02126
02127 case VE_TYPE_ELECTRIC:
02128
02129
02130
02131
02132
02133
02134 if (GB(v->tick_counter, 0, 2) == 0 &&
02135 Chance16((6 - ((this->cur_speed << 2) / this->vcache.cached_max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
02136 CreateEffectVehicleRel(v, x, y, 10, EV_ELECTRIC_SPARK);
02137 sound = true;
02138 }
02139 break;
02140
02141 default:
02142 break;
02143 }
02144 } while ((v = v->Next()) != NULL);
02145
02146 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
02147 }
02148
02153 void Vehicle::SetNext(Vehicle *next)
02154 {
02155 assert(this != next);
02156
02157 if (this->next != NULL) {
02158
02159 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02160 v->first = this->next;
02161 }
02162 this->next->previous = NULL;
02163 }
02164
02165 this->next = next;
02166
02167 if (this->next != NULL) {
02168
02169 if (this->next->previous != NULL) this->next->previous->next = NULL;
02170 this->next->previous = this;
02171 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02172 v->first = this->first;
02173 }
02174 }
02175 }
02176
02182 void Vehicle::AddToShared(Vehicle *shared_chain)
02183 {
02184 assert(this->previous_shared == NULL && this->next_shared == NULL);
02185
02186 if (!shared_chain->orders.list) {
02187 assert(shared_chain->previous_shared == NULL);
02188 assert(shared_chain->next_shared == NULL);
02189 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
02190 }
02191
02192 this->next_shared = shared_chain->next_shared;
02193 this->previous_shared = shared_chain;
02194
02195 shared_chain->next_shared = this;
02196
02197 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
02198
02199 shared_chain->orders.list->AddVehicle(this);
02200 }
02201
02205 void Vehicle::RemoveFromShared()
02206 {
02207
02208
02209 bool were_first = (this->FirstShared() == this);
02210 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
02211
02212 this->orders.list->RemoveVehicle(this);
02213
02214 if (!were_first) {
02215
02216 this->previous_shared->next_shared = this->NextShared();
02217 }
02218
02219 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
02220
02221
02222 if (this->orders.list->GetNumVehicles() == 1) {
02223
02224 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
02225 InvalidateVehicleOrder(this->FirstShared(), 0);
02226 } else if (were_first) {
02227
02228
02229 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
02230 }
02231
02232 this->next_shared = NULL;
02233 this->previous_shared = NULL;
02234 }
02235
02241 Order *Vehicle::GetNextManualOrder(int index) const
02242 {
02243 Order *order = this->GetOrder(index);
02244 while(order != NULL && order->IsType(OT_AUTOMATIC)) {
02245 order = order->next;
02246 }
02247 return order;
02248 }
02249
02250 void VehiclesYearlyLoop()
02251 {
02252 Vehicle *v;
02253 FOR_ALL_VEHICLES(v) {
02254 if (v->IsPrimaryVehicle()) {
02255
02256 Money profit = v->GetDisplayProfitThisYear();
02257 if (v->age >= 730 && profit < 0) {
02258 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
02259 SetDParam(0, v->index);
02260 SetDParam(1, profit);
02261 AddVehicleNewsItem(
02262 STR_NEWS_VEHICLE_IS_UNPROFITABLE,
02263 NS_ADVICE,
02264 v->index
02265 );
02266 }
02267 AI::NewEvent(v->owner, new AIEventVehicleUnprofitable(v->index));
02268 }
02269
02270 v->profit_last_year = v->profit_this_year;
02271 v->profit_this_year = 0;
02272 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
02273 }
02274 }
02275 }
02276
02277
02287 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
02288 {
02289 const Engine *e = Engine::GetIfValid(engine_type);
02290 assert(e != NULL);
02291
02292 switch (e->type) {
02293 case VEH_TRAIN:
02294 return (st->facilities & FACIL_TRAIN) != 0;
02295
02296 case VEH_ROAD:
02297
02298
02299
02300 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
02301
02302 case VEH_SHIP:
02303 return (st->facilities & FACIL_DOCK) != 0;
02304
02305 case VEH_AIRCRAFT:
02306 return (st->facilities & FACIL_AIRPORT) != 0 &&
02307 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
02308
02309 default:
02310 return false;
02311 }
02312 }
02313
02320 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
02321 {
02322 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
02323
02324 return CanVehicleUseStation(v->engine_type, st);
02325 }
02326
02332 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
02333 {
02334 assert(this->IsGroundVehicle());
02335 if (this->type == VEH_TRAIN) {
02336 return &Train::From(this)->gcache;
02337 } else {
02338 return &RoadVehicle::From(this)->gcache;
02339 }
02340 }
02341
02347 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
02348 {
02349 assert(this->IsGroundVehicle());
02350 if (this->type == VEH_TRAIN) {
02351 return &Train::From(this)->gcache;
02352 } else {
02353 return &RoadVehicle::From(this)->gcache;
02354 }
02355 }
02356
02365 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
02366 {
02367 if (v->type == VEH_TRAIN) {
02368 Train *u = Train::From(v);
02369
02370 if (u->IsArticulatedPart()) {
02371 u = u->GetFirstEnginePart();
02372 while (u->index != v->index) {
02373 set.Include(u->index);
02374 u = u->GetNextArticulatedPart();
02375 }
02376 }
02377
02378 for (;u != NULL && num_vehicles > 0; num_vehicles--, u = u->Next()) {
02379
02380 set.Include(u->index);
02381
02382
02383 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
02384 }
02385
02386
02387 while (u != NULL && u->IsArticulatedPart()) {
02388 set.Include(u->index);
02389 u = u->Next();
02390 }
02391 }
02392 }