00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include "gui.h"
00014 #include "roadveh.h"
00015 #include "ship.h"
00016 #include "spritecache.h"
00017 #include "timetable.h"
00018 #include "viewport_func.h"
00019 #include "news_func.h"
00020 #include "command_func.h"
00021 #include "company_func.h"
00022 #include "vehicle_gui.h"
00023 #include "train.h"
00024 #include "aircraft.h"
00025 #include "newgrf_debug.h"
00026 #include "newgrf_sound.h"
00027 #include "newgrf_station.h"
00028 #include "group.h"
00029 #include "group_gui.h"
00030 #include "strings_func.h"
00031 #include "zoom_func.h"
00032 #include "date_func.h"
00033 #include "window_func.h"
00034 #include "vehicle_func.h"
00035 #include "autoreplace_func.h"
00036 #include "autoreplace_gui.h"
00037 #include "station_base.h"
00038 #include "ai/ai.hpp"
00039 #include "depot_func.h"
00040 #include "network/network.h"
00041 #include "core/pool_func.hpp"
00042 #include "economy_base.h"
00043 #include "articulated_vehicles.h"
00044 #include "roadstop_base.h"
00045 #include "core/random_func.hpp"
00046 #include "core/backup_type.hpp"
00047 #include "order_backup.h"
00048 #include "sound_func.h"
00049 #include "effectvehicle_func.h"
00050 #include "effectvehicle_base.h"
00051 #include "vehiclelist.h"
00052 #include "bridge_map.h"
00053 #include "tunnel_map.h"
00054 #include "depot_map.h"
00055
00056 #include "table/strings.h"
00057
00058 #define GEN_HASH(x, y) ((GB((y), 6, 6) << 6) + GB((x), 7, 6))
00059
00060 VehicleID _new_vehicle_id;
00061 uint16 _returned_refit_capacity;
00062 uint16 _returned_mail_refit_capacity;
00063 byte _age_cargo_skip_counter;
00064
00065
00067 VehiclePool _vehicle_pool("Vehicle");
00068 INSTANTIATE_POOL_METHODS(Vehicle)
00069
00070
00075 bool Vehicle::NeedsAutorenewing(const Company *c) const
00076 {
00077
00078
00079
00080
00081 assert(c == Company::Get(this->owner));
00082
00083 if (!c->settings.engine_renew) return false;
00084 if (this->age - this->max_age < (c->settings.engine_renew_months * 30)) return false;
00085 if (this->age == 0) return false;
00086
00087 return true;
00088 }
00089
00090 void VehicleServiceInDepot(Vehicle *v)
00091 {
00092 v->date_of_last_service = _date;
00093 v->breakdowns_since_last_service = 0;
00094 v->reliability = Engine::Get(v->engine_type)->reliability;
00095 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
00096 }
00097
00104 bool Vehicle::NeedsServicing() const
00105 {
00106
00107
00108 if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
00109
00110
00111 const Company *c = Company::Get(this->owner);
00112 if (c->settings.vehicle.servint_ispercent ?
00113 (this->reliability >= Engine::Get(this->engine_type)->reliability * (100 - this->service_interval) / 100) :
00114 (this->date_of_last_service + this->service_interval >= _date)) {
00115 return false;
00116 }
00117
00118
00119
00120 if (!_settings_game.order.no_servicing_if_no_breakdowns ||
00121 _settings_game.difficulty.vehicle_breakdowns != 0) {
00122 return true;
00123 }
00124
00125
00126
00127
00128 bool pending_replace = false;
00129 Money needed_money = c->settings.engine_renew_money;
00130 if (needed_money > c->money) return false;
00131
00132 for (const Vehicle *v = this; v != NULL; v = (v->type == VEH_TRAIN) ? Train::From(v)->GetNextUnit() : NULL) {
00133 EngineID new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id);
00134
00135
00136 if (new_engine == INVALID_ENGINE || !HasBit(Engine::Get(new_engine)->company_avail, v->owner)) continue;
00137
00138
00139 uint32 available_cargo_types, union_mask;
00140 GetArticulatedRefitMasks(new_engine, true, &union_mask, &available_cargo_types);
00141
00142 if (union_mask != 0) {
00143 CargoID cargo_type;
00144
00145 if (IsArticulatedVehicleCarryingDifferentCargos(v, &cargo_type)) continue;
00146
00147
00148 if (cargo_type != CT_INVALID) {
00149
00150 if (!HasBit(available_cargo_types, cargo_type)) continue;
00151 }
00152 }
00153
00154
00155
00156 pending_replace = true;
00157 needed_money += 2 * Engine::Get(new_engine)->GetCost();
00158 if (needed_money > c->money) return false;
00159 }
00160
00161 return pending_replace;
00162 }
00163
00169 bool Vehicle::NeedsAutomaticServicing() const
00170 {
00171 if (this->HasDepotOrder()) return false;
00172 if (this->current_order.IsType(OT_LOADING)) return false;
00173 if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
00174 return NeedsServicing();
00175 }
00176
00177 uint Vehicle::Crash(bool flooded)
00178 {
00179 assert((this->vehstatus & VS_CRASHED) == 0);
00180 assert(this->Previous() == NULL);
00181
00182 uint pass = 0;
00183
00184 if (this->IsPrimaryVehicle()) this->vehstatus |= VS_STOPPED;
00185
00186 for (Vehicle *v = this; v != NULL; v = v->Next()) {
00187 if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo.Count();
00188 v->vehstatus |= VS_CRASHED;
00189 MarkSingleVehicleDirty(v);
00190 }
00191
00192
00193 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00194 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
00195 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
00196 SetWindowDirty(WC_VEHICLE_DEPOT, this->tile);
00197
00198 return pass;
00199 }
00200
00201
00210 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
00211 {
00212 const Engine *e = Engine::Get(engine);
00213 uint32 grfid = e->grf_prop.grffile->grfid;
00214 GRFConfig *grfconfig = GetGRFConfig(grfid);
00215
00216 if (!HasBit(grfconfig->grf_bugs, bug_type)) {
00217 SetBit(grfconfig->grf_bugs, bug_type);
00218 SetDParamStr(0, grfconfig->GetName());
00219 SetDParam(1, engine);
00220 ShowErrorMessage(part1, part2, WL_CRITICAL);
00221 if (!_networking) DoCommand(0, critical ? PM_PAUSED_ERROR : PM_PAUSED_NORMAL, 1, DC_EXEC, CMD_PAUSE);
00222 }
00223
00224
00225 char buffer[512];
00226
00227 SetDParamStr(0, grfconfig->GetName());
00228 GetString(buffer, part1, lastof(buffer));
00229 DEBUG(grf, 0, "%s", buffer + 3);
00230
00231 SetDParam(1, engine);
00232 GetString(buffer, part2, lastof(buffer));
00233 DEBUG(grf, 0, "%s", buffer + 3);
00234 }
00235
00240 Vehicle::Vehicle(VehicleType type)
00241 {
00242 this->type = type;
00243 this->coord.left = INVALID_COORD;
00244 this->group_id = DEFAULT_GROUP;
00245 this->fill_percent_te_id = INVALID_TE_ID;
00246 this->first = this;
00247 this->colourmap = PAL_NONE;
00248 }
00249
00254 byte VehicleRandomBits()
00255 {
00256 return GB(Random(), 0, 8);
00257 }
00258
00259
00260
00261 const int HASH_BITS = 7;
00262 const int HASH_SIZE = 1 << HASH_BITS;
00263 const int HASH_MASK = HASH_SIZE - 1;
00264 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
00265 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
00266
00267
00268
00269 const int HASH_RES = 0;
00270
00271 static Vehicle *_new_vehicle_position_hash[TOTAL_HASH_SIZE];
00272
00273 static Vehicle *VehicleFromHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
00274 {
00275 for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
00276 for (int x = xl; ; x = (x + 1) & HASH_MASK) {
00277 Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00278 for (; v != NULL; v = v->next_new_hash) {
00279 Vehicle *a = proc(v, data);
00280 if (find_first && a != NULL) return a;
00281 }
00282 if (x == xu) break;
00283 }
00284 if (y == yu) break;
00285 }
00286
00287 return NULL;
00288 }
00289
00290
00302 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
00303 {
00304 const int COLL_DIST = 6;
00305
00306
00307 int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00308 int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00309 int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00310 int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00311
00312 return VehicleFromHash(xl, yl, xu, yu, data, proc, find_first);
00313 }
00314
00329 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00330 {
00331 VehicleFromPosXY(x, y, data, proc, false);
00332 }
00333
00345 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00346 {
00347 return VehicleFromPosXY(x, y, data, proc, true) != NULL;
00348 }
00349
00360 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
00361 {
00362 int x = GB(TileX(tile), HASH_RES, HASH_BITS);
00363 int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
00364
00365 Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00366 for (; v != NULL; v = v->next_new_hash) {
00367 if (v->tile != tile) continue;
00368
00369 Vehicle *a = proc(v, data);
00370 if (find_first && a != NULL) return a;
00371 }
00372
00373 return NULL;
00374 }
00375
00389 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00390 {
00391 VehicleFromPos(tile, data, proc, false);
00392 }
00393
00404 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00405 {
00406 return VehicleFromPos(tile, data, proc, true) != NULL;
00407 }
00408
00415 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
00416 {
00417 byte z = *(byte*)data;
00418
00419 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
00420 if (v->z_pos > z) return NULL;
00421
00422 return v;
00423 }
00424
00430 CommandCost EnsureNoVehicleOnGround(TileIndex tile)
00431 {
00432 byte z = GetTileMaxZ(tile);
00433
00434
00435
00436
00437
00438 Vehicle *v = VehicleFromPos(tile, &z, &EnsureNoVehicleProcZ, true);
00439 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00440 return CommandCost();
00441 }
00442
00444 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
00445 {
00446 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
00447 if (v == (const Vehicle *)data) return NULL;
00448
00449 return v;
00450 }
00451
00459 CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
00460 {
00461
00462
00463
00464
00465 Vehicle *v = VehicleFromPos(tile, (void *)ignore, &GetVehicleTunnelBridgeProc, true);
00466 if (v == NULL) v = VehicleFromPos(endtile, (void *)ignore, &GetVehicleTunnelBridgeProc, true);
00467
00468 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00469 return CommandCost();
00470 }
00471
00472 static Vehicle *EnsureNoTrainOnTrackProc(Vehicle *v, void *data)
00473 {
00474 TrackBits rail_bits = *(TrackBits *)data;
00475
00476 if (v->type != VEH_TRAIN) return NULL;
00477
00478 Train *t = Train::From(v);
00479 if ((t->track != rail_bits) && !TracksOverlap(t->track | rail_bits)) return NULL;
00480
00481 return v;
00482 }
00483
00492 CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
00493 {
00494
00495
00496
00497
00498 Vehicle *v = VehicleFromPos(tile, &track_bits, &EnsureNoTrainOnTrackProc, true);
00499 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00500 return CommandCost();
00501 }
00502
00503 static void UpdateNewVehiclePosHash(Vehicle *v, bool remove)
00504 {
00505 Vehicle **old_hash = v->old_new_hash;
00506 Vehicle **new_hash;
00507
00508 if (remove) {
00509 new_hash = NULL;
00510 } else {
00511 int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
00512 int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
00513 new_hash = &_new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00514 }
00515
00516 if (old_hash == new_hash) return;
00517
00518
00519 if (old_hash != NULL) {
00520 if (v->next_new_hash != NULL) v->next_new_hash->prev_new_hash = v->prev_new_hash;
00521 *v->prev_new_hash = v->next_new_hash;
00522 }
00523
00524
00525 if (new_hash != NULL) {
00526 v->next_new_hash = *new_hash;
00527 if (v->next_new_hash != NULL) v->next_new_hash->prev_new_hash = &v->next_new_hash;
00528 v->prev_new_hash = new_hash;
00529 *new_hash = v;
00530 }
00531
00532
00533 v->old_new_hash = new_hash;
00534 }
00535
00536 static Vehicle *_vehicle_position_hash[0x1000];
00537
00538 static void UpdateVehiclePosHash(Vehicle *v, int x, int y)
00539 {
00540 UpdateNewVehiclePosHash(v, x == INVALID_COORD);
00541
00542 Vehicle **old_hash, **new_hash;
00543 int old_x = v->coord.left;
00544 int old_y = v->coord.top;
00545
00546 new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(x, y)];
00547 old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(old_x, old_y)];
00548
00549 if (old_hash == new_hash) return;
00550
00551
00552 if (old_hash != NULL) {
00553 if (v->next_hash != NULL) v->next_hash->prev_hash = v->prev_hash;
00554 *v->prev_hash = v->next_hash;
00555 }
00556
00557
00558 if (new_hash != NULL) {
00559 v->next_hash = *new_hash;
00560 if (v->next_hash != NULL) v->next_hash->prev_hash = &v->next_hash;
00561 v->prev_hash = new_hash;
00562 *new_hash = v;
00563 }
00564 }
00565
00566 void ResetVehiclePosHash()
00567 {
00568 Vehicle *v;
00569 FOR_ALL_VEHICLES(v) { v->old_new_hash = NULL; }
00570 memset(_vehicle_position_hash, 0, sizeof(_vehicle_position_hash));
00571 memset(_new_vehicle_position_hash, 0, sizeof(_new_vehicle_position_hash));
00572 }
00573
00574 void ResetVehicleColourMap()
00575 {
00576 Vehicle *v;
00577 FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
00578 }
00579
00584 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
00585 static AutoreplaceMap _vehicles_to_autoreplace;
00586
00587 void InitializeVehicles()
00588 {
00589 _vehicle_pool.CleanPool();
00590 _cargo_payment_pool.CleanPool();
00591
00592 _age_cargo_skip_counter = 1;
00593
00594 _vehicles_to_autoreplace.Reset();
00595 ResetVehiclePosHash();
00596 }
00597
00598 uint CountVehiclesInChain(const Vehicle *v)
00599 {
00600 uint count = 0;
00601 do count++; while ((v = v->Next()) != NULL);
00602 return count;
00603 }
00604
00610 void CountCompanyVehicles(CompanyID cid, uint counts[4])
00611 {
00612 for (uint i = 0; i < 4; i++) counts[i] = 0;
00613
00614 const Vehicle *v;
00615 FOR_ALL_VEHICLES(v) {
00616 if (v->owner == cid && v->IsPrimaryVehicle()) counts[v->type]++;
00617 }
00618 }
00619
00624 bool Vehicle::IsEngineCountable() const
00625 {
00626 switch (this->type) {
00627 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
00628 case VEH_TRAIN:
00629 return !this->IsArticulatedPart() &&
00630 !Train::From(this)->IsRearDualheaded();
00631 case VEH_ROAD: return RoadVehicle::From(this)->IsFrontEngine();
00632 case VEH_SHIP: return true;
00633 default: return false;
00634 }
00635 }
00636
00644 void Vehicle::HandlePathfindingResult(bool path_found)
00645 {
00646 if (path_found) {
00647
00648 if (!HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00649
00650
00651 ClrBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00652
00653 DeleteVehicleNews(this->index, STR_NEWS_VEHICLE_IS_LOST);
00654 return;
00655 }
00656
00657
00658 if (HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00659
00660
00661 SetBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00662
00663 AI::NewEvent(this->owner, new AIEventVehicleLost(this->index));
00664 if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) {
00665 SetDParam(0, this->index);
00666 AddVehicleNewsItem(STR_NEWS_VEHICLE_IS_LOST, NS_ADVICE, this->index);
00667 }
00668 }
00669
00671 void Vehicle::PreDestructor()
00672 {
00673 if (CleaningPool()) return;
00674
00675 if (Station::IsValidID(this->last_station_visited)) {
00676 Station::Get(this->last_station_visited)->loading_vehicles.remove(this);
00677
00678 HideFillingPercent(&this->fill_percent_te_id);
00679
00680 delete this->cargo_payment;
00681 }
00682
00683 if (this->IsEngineCountable()) {
00684 Company::Get(this->owner)->num_engines[this->engine_type]--;
00685 if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
00686
00687 DeleteGroupHighlightOfVehicle(this);
00688 if (Group::IsValidID(this->group_id)) Group::Get(this->group_id)->num_engines[this->engine_type]--;
00689 if (this->IsPrimaryVehicle()) DecreaseGroupNumVehicle(this->group_id);
00690 }
00691
00692 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
00693 Aircraft *a = Aircraft::From(this);
00694 Station *st = GetTargetAirportIfValid(a);
00695 if (st != NULL) {
00696 const AirportFTA *layout = st->airport.GetFTA()->layout;
00697 CLRBITS(st->airport.flags, layout[a->previous_pos].block | layout[a->pos].block);
00698 }
00699 }
00700
00701
00702 if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
00703 RoadVehicle *v = RoadVehicle::From(this);
00704 if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
00705
00706 RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
00707 }
00708 }
00709
00710 if (this->Previous() == NULL) {
00711 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
00712 }
00713
00714 if (this->IsPrimaryVehicle()) {
00715 DeleteWindowById(WC_VEHICLE_VIEW, this->index);
00716 DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
00717 DeleteWindowById(WC_VEHICLE_REFIT, this->index);
00718 DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
00719 DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
00720 SetWindowDirty(WC_COMPANY, this->owner);
00721 OrderBackup::ClearVehicle(this);
00722 }
00723 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00724
00725 this->cargo.Truncate(0);
00726 DeleteVehicleOrders(this);
00727 DeleteDepotHighlightOfVehicle(this);
00728
00729 extern void StopGlobalFollowVehicle(const Vehicle *v);
00730 StopGlobalFollowVehicle(this);
00731
00732 ReleaseDisastersTargetingVehicle(this->index);
00733 }
00734
00735 Vehicle::~Vehicle()
00736 {
00737 free(this->name);
00738
00739 if (CleaningPool()) return;
00740
00741
00742
00743 if (!(this->vehstatus & VS_HIDDEN)) MarkSingleVehicleDirty(this);
00744
00745 Vehicle *v = this->Next();
00746 this->SetNext(NULL);
00747
00748 delete v;
00749
00750 UpdateVehiclePosHash(this, INVALID_COORD, 0);
00751 DeleteVehicleNews(this->index, INVALID_STRING_ID);
00752 DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index);
00753 }
00754
00759 void VehicleEnteredDepotThisTick(Vehicle *v)
00760 {
00761
00762 _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
00763
00764
00765
00766
00767
00768
00769 v->vehstatus |= VS_STOPPED;
00770 }
00771
00777 static void RunVehicleDayProc()
00778 {
00779 if (_game_mode != GM_NORMAL) return;
00780
00781
00782 for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
00783 Vehicle *v = Vehicle::Get(i);
00784 if (v == NULL) continue;
00785
00786
00787 if ((v->day_counter & 0x1F) == 0) {
00788 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
00789 if (callback != CALLBACK_FAILED) {
00790 if (HasBit(callback, 0)) TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32);
00791 if (HasBit(callback, 1)) v->colourmap = PAL_NONE;
00792 }
00793 }
00794
00795
00796 v->OnNewDay();
00797 }
00798 }
00799
00800 void CallVehicleTicks()
00801 {
00802 _vehicles_to_autoreplace.Clear();
00803
00804 _age_cargo_skip_counter = (_age_cargo_skip_counter == 0) ? 184 : (_age_cargo_skip_counter - 1);
00805
00806 RunVehicleDayProc();
00807
00808 Station *st;
00809 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
00810
00811 Vehicle *v;
00812 FOR_ALL_VEHICLES(v) {
00813
00814 if (!v->Tick()) {
00815 assert(Vehicle::Get(vehicle_index) == NULL);
00816 continue;
00817 }
00818
00819 assert(Vehicle::Get(vehicle_index) == v);
00820
00821 switch (v->type) {
00822 default: break;
00823
00824 case VEH_TRAIN:
00825 case VEH_ROAD:
00826 case VEH_AIRCRAFT:
00827 case VEH_SHIP:
00828 if (_age_cargo_skip_counter == 0) v->cargo.AgeCargo();
00829
00830 if (v->type == VEH_TRAIN && Train::From(v)->IsWagon()) continue;
00831 if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
00832 if (v->type == VEH_ROAD && !RoadVehicle::From(v)->IsFrontEngine()) continue;
00833
00834 v->motion_counter += v->cur_speed;
00835
00836 if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
00837
00838
00839 if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
00840 }
00841 }
00842
00843 Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
00844 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
00845 v = it->first;
00846
00847 cur_company.Change(v->owner);
00848
00849
00850
00851
00852 if (it->second) v->vehstatus &= ~VS_STOPPED;
00853
00854
00855 int x = v->x_pos;
00856 int y = v->y_pos;
00857 int z = v->z_pos;
00858
00859 const Company *c = Company::Get(_current_company);
00860 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
00861 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
00862 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
00863
00864 if (!IsLocalCompany()) continue;
00865
00866 if (res.Succeeded()) {
00867 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
00868 continue;
00869 }
00870
00871 StringID error_message = res.GetErrorMessage();
00872 if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
00873
00874 if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
00875
00876 StringID message;
00877 if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
00878 message = error_message;
00879 } else {
00880 message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
00881 }
00882
00883 SetDParam(0, v->index);
00884 SetDParam(1, error_message);
00885 AddVehicleNewsItem(message, NS_ADVICE, v->index);
00886 }
00887
00888 cur_company.Restore();
00889 }
00890
00895 static void DoDrawVehicle(const Vehicle *v)
00896 {
00897 SpriteID image = v->cur_image;
00898 PaletteID pal = PAL_NONE;
00899
00900 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
00901
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::CanAllocateItem()) {
01854 Order *auto_order = new Order();
01855 auto_order->MakeAutomatic(this->last_station_visited);
01856 InsertOrder(this, auto_order, this->cur_auto_order_index);
01857 if (this->cur_auto_order_index > 0) --this->cur_auto_order_index;
01858 }
01859 this->current_order.MakeLoading(false);
01860 }
01861
01862 Station::Get(this->last_station_visited)->loading_vehicles.push_back(this);
01863
01864 PrepareUnload(this);
01865
01866 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
01867 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01868 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01869 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
01870
01871 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
01872 this->cur_speed = 0;
01873 this->MarkDirty();
01874 }
01875
01880 void Vehicle::LeaveStation()
01881 {
01882 assert(this->current_order.IsType(OT_LOADING));
01883
01884 delete this->cargo_payment;
01885
01886
01887 if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
01888
01889 this->current_order.MakeLeaveStation();
01890 Station *st = Station::Get(this->last_station_visited);
01891 st->loading_vehicles.remove(this);
01892
01893 HideFillingPercent(&this->fill_percent_te_id);
01894
01895 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
01896
01897 if (IsTileType(this->tile, MP_STATION)) TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
01898
01899 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
01900 }
01901 }
01902
01903
01909 void Vehicle::HandleLoading(bool mode)
01910 {
01911 switch (this->current_order.GetType()) {
01912 case OT_LOADING: {
01913 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
01914
01915
01916 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
01917
01918 this->PlayLeaveStationSound();
01919
01920 this->LeaveStation();
01921
01922 break;
01923 }
01924
01925 case OT_DUMMY: break;
01926
01927 default: return;
01928 }
01929
01930 this->IncrementAutoOrderIndex();
01931 }
01932
01939 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
01940 {
01941 CommandCost ret = CheckOwnership(this->owner);
01942 if (ret.Failed()) return ret;
01943
01944 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
01945 if (this->IsStoppedInDepot()) return CMD_ERROR;
01946
01947 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
01948 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
01949 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
01950
01951
01952
01953 if (flags & DC_EXEC) {
01954 this->current_order.SetDepotOrderType(ODTF_MANUAL);
01955 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
01956 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01957 }
01958 return CommandCost();
01959 }
01960
01961 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR;
01962 if (flags & DC_EXEC) {
01963
01964
01965 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
01966
01967 this->current_order.MakeDummy();
01968 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01969 }
01970 return CommandCost();
01971 }
01972
01973 TileIndex location;
01974 DestinationID destination;
01975 bool reverse;
01976 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};
01977 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
01978
01979 if (flags & DC_EXEC) {
01980 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
01981
01982 this->dest_tile = location;
01983 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
01984 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
01985 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01986
01987
01988 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
01989
01990 if (this->type == VEH_AIRCRAFT) {
01991 Aircraft *a = Aircraft::From(this);
01992 if (a->state == FLYING && a->targetairport != destination) {
01993
01994 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
01995 AircraftNextAirportPos_and_Order(a);
01996 }
01997 }
01998 }
01999
02000 return CommandCost();
02001
02002 }
02003
02008 void Vehicle::UpdateVisualEffect(bool allow_power_change)
02009 {
02010 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02011 const Engine *e = Engine::Get(this->engine_type);
02012
02013
02014 byte visual_effect;
02015 switch (e->type) {
02016 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
02017 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
02018 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
02019 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
02020 }
02021
02022
02023 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
02024 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
02025
02026 if (callback != CALLBACK_FAILED) {
02027 callback = GB(callback, 0, 8);
02028
02029
02030 if (callback == VE_DEFAULT) {
02031 assert(HasBit(callback, VE_DISABLE_EFFECT));
02032 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
02033 }
02034 visual_effect = callback;
02035 }
02036 }
02037
02038
02039 if (visual_effect == VE_DEFAULT ||
02040 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
02041
02042
02043 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
02044 if (visual_effect == VE_DEFAULT) {
02045 visual_effect = 1 << VE_DISABLE_EFFECT;
02046 } else {
02047 SetBit(visual_effect, VE_DISABLE_EFFECT);
02048 }
02049 } else {
02050 if (visual_effect == VE_DEFAULT) {
02051
02052 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
02053 }
02054 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
02055 }
02056 }
02057
02058 this->vcache.cached_vis_effect = visual_effect;
02059
02060 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
02061 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02062 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
02063 }
02064 }
02065
02066 static const int8 _vehicle_smoke_pos[8] = {
02067 1, 1, 1, 0, -1, -1, -1, 0
02068 };
02069
02074 void Vehicle::ShowVisualEffect() const
02075 {
02076 assert(this->IsPrimaryVehicle());
02077 bool sound = false;
02078
02079
02080
02081
02082
02083
02084 if (_settings_game.vehicle.smoke_amount == 0 ||
02085 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
02086 this->cur_speed < 2) {
02087 return;
02088 }
02089 if (this->type == VEH_TRAIN) {
02090 const Train *t = Train::From(this);
02091
02092
02093
02094
02095 if (HasBit(t->flags, VRF_REVERSING) ||
02096 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
02097 t->cur_speed >= t->Train::GetCurrentMaxSpeed())) {
02098 return;
02099 }
02100 }
02101
02102 const Vehicle *v = this;
02103
02104 do {
02105 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
02106 byte effect_type = GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
02107 bool disable_effect = HasBit(v->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
02108
02109
02110
02111
02112
02113
02114
02115
02116 if (disable_effect ||
02117 v->vehstatus & VS_HIDDEN ||
02118 (MayHaveBridgeAbove(v->tile) && IsBridgeAbove(v->tile)) ||
02119 IsDepotTile(v->tile) ||
02120 IsTunnelTile(v->tile) ||
02121 (v->type == VEH_TRAIN &&
02122 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
02123 continue;
02124 }
02125
02126 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
02127 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
02128
02129 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
02130 x = -x;
02131 y = -y;
02132 }
02133
02134 switch (effect_type) {
02135 case VE_TYPE_STEAM:
02136
02137
02138
02139
02140
02141 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / this->vcache.cached_max_speed))) == 0) {
02142 CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
02143 sound = true;
02144 }
02145 break;
02146
02147 case VE_TYPE_DIESEL: {
02148
02149
02150
02151
02152
02153
02154
02155
02156
02157
02158
02159 int power_weight_effect = 0;
02160 if (v->type == VEH_TRAIN) {
02161 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
02162 }
02163 if (this->cur_speed < (this->vcache.cached_max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
02164 Chance16((64 - ((this->cur_speed << 5) / this->vcache.cached_max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
02165 CreateEffectVehicleRel(v, x, y, 10, EV_DIESEL_SMOKE);
02166 sound = true;
02167 }
02168 break;
02169 }
02170
02171 case VE_TYPE_ELECTRIC:
02172
02173
02174
02175
02176
02177
02178 if (GB(v->tick_counter, 0, 2) == 0 &&
02179 Chance16((6 - ((this->cur_speed << 2) / this->vcache.cached_max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
02180 CreateEffectVehicleRel(v, x, y, 10, EV_ELECTRIC_SPARK);
02181 sound = true;
02182 }
02183 break;
02184
02185 default:
02186 break;
02187 }
02188 } while ((v = v->Next()) != NULL);
02189
02190 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
02191 }
02192
02197 void Vehicle::SetNext(Vehicle *next)
02198 {
02199 assert(this != next);
02200
02201 if (this->next != NULL) {
02202
02203 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02204 v->first = this->next;
02205 }
02206 this->next->previous = NULL;
02207 }
02208
02209 this->next = next;
02210
02211 if (this->next != NULL) {
02212
02213 if (this->next->previous != NULL) this->next->previous->next = NULL;
02214 this->next->previous = this;
02215 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02216 v->first = this->first;
02217 }
02218 }
02219 }
02220
02226 void Vehicle::AddToShared(Vehicle *shared_chain)
02227 {
02228 assert(this->previous_shared == NULL && this->next_shared == NULL);
02229
02230 if (shared_chain->orders.list == NULL) {
02231 assert(shared_chain->previous_shared == NULL);
02232 assert(shared_chain->next_shared == NULL);
02233 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
02234 }
02235
02236 this->next_shared = shared_chain->next_shared;
02237 this->previous_shared = shared_chain;
02238
02239 shared_chain->next_shared = this;
02240
02241 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
02242
02243 shared_chain->orders.list->AddVehicle(this);
02244 }
02245
02249 void Vehicle::RemoveFromShared()
02250 {
02251
02252
02253 bool were_first = (this->FirstShared() == this);
02254 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
02255
02256 this->orders.list->RemoveVehicle(this);
02257
02258 if (!were_first) {
02259
02260 this->previous_shared->next_shared = this->NextShared();
02261 }
02262
02263 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
02264
02265
02266 if (this->orders.list->GetNumVehicles() == 1) {
02267
02268 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
02269 InvalidateVehicleOrder(this->FirstShared(), 0);
02270 } else if (were_first) {
02271
02272
02273 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
02274 }
02275
02276 this->next_shared = NULL;
02277 this->previous_shared = NULL;
02278 }
02279
02280 void VehiclesYearlyLoop()
02281 {
02282 Vehicle *v;
02283 FOR_ALL_VEHICLES(v) {
02284 if (v->IsPrimaryVehicle()) {
02285
02286 Money profit = v->GetDisplayProfitThisYear();
02287 if (v->age >= 730 && profit < 0) {
02288 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
02289 SetDParam(0, v->index);
02290 SetDParam(1, profit);
02291 AddVehicleNewsItem(
02292 STR_NEWS_VEHICLE_IS_UNPROFITABLE,
02293 NS_ADVICE,
02294 v->index
02295 );
02296 }
02297 AI::NewEvent(v->owner, new AIEventVehicleUnprofitable(v->index));
02298 }
02299
02300 v->profit_last_year = v->profit_this_year;
02301 v->profit_this_year = 0;
02302 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
02303 }
02304 }
02305 }
02306
02307
02317 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
02318 {
02319 const Engine *e = Engine::GetIfValid(engine_type);
02320 assert(e != NULL);
02321
02322 switch (e->type) {
02323 case VEH_TRAIN:
02324 return (st->facilities & FACIL_TRAIN) != 0;
02325
02326 case VEH_ROAD:
02327
02328
02329
02330 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
02331
02332 case VEH_SHIP:
02333 return (st->facilities & FACIL_DOCK) != 0;
02334
02335 case VEH_AIRCRAFT:
02336 return (st->facilities & FACIL_AIRPORT) != 0 &&
02337 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
02338
02339 default:
02340 return false;
02341 }
02342 }
02343
02350 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
02351 {
02352 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
02353
02354 return CanVehicleUseStation(v->engine_type, st);
02355 }
02356
02362 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
02363 {
02364 assert(this->IsGroundVehicle());
02365 if (this->type == VEH_TRAIN) {
02366 return &Train::From(this)->gcache;
02367 } else {
02368 return &RoadVehicle::From(this)->gcache;
02369 }
02370 }
02371
02377 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
02378 {
02379 assert(this->IsGroundVehicle());
02380 if (this->type == VEH_TRAIN) {
02381 return &Train::From(this)->gcache;
02382 } else {
02383 return &RoadVehicle::From(this)->gcache;
02384 }
02385 }
02386
02395 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
02396 {
02397 if (v->type == VEH_TRAIN) {
02398 Train *u = Train::From(v);
02399
02400 u = u->GetFirstEnginePart();
02401
02402
02403 for (; u != NULL && num_vehicles > 0; num_vehicles--) {
02404 do {
02405
02406 set.Include(u->index);
02407
02408
02409 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
02410
02411 u = u->Next();
02412 } while (u != NULL && u->IsArticulatedPart());
02413 }
02414 }
02415 }