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 #include "ground_vehicle.hpp"
00056
00057 #include "table/strings.h"
00058
00059 #define GEN_HASH(x, y) ((GB((y), 6, 6) << 6) + GB((x), 7, 6))
00060
00061 VehicleID _new_vehicle_id;
00062 uint16 _returned_refit_capacity;
00063 uint16 _returned_mail_refit_capacity;
00064 byte _age_cargo_skip_counter;
00065
00066
00067
00068 VehiclePool _vehicle_pool("Vehicle");
00069 INSTANTIATE_POOL_METHODS(Vehicle)
00070
00071
00076 bool Vehicle::NeedsAutorenewing(const Company *c) const
00077 {
00078
00079
00080
00081
00082 assert(c == Company::Get(this->owner));
00083
00084 if (!c->settings.engine_renew) return false;
00085 if (this->age - this->max_age < (c->settings.engine_renew_months * 30)) return false;
00086 if (this->age == 0) return false;
00087
00088 return true;
00089 }
00090
00091 void VehicleServiceInDepot(Vehicle *v)
00092 {
00093 v->date_of_last_service = _date;
00094 v->breakdowns_since_last_service = 0;
00095 v->reliability = Engine::Get(v->engine_type)->reliability;
00096 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
00097 }
00098
00105 bool Vehicle::NeedsServicing() const
00106 {
00107
00108
00109 if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
00110
00111
00112 const Company *c = Company::Get(this->owner);
00113 if (c->settings.vehicle.servint_ispercent ?
00114 (this->reliability >= Engine::Get(this->engine_type)->reliability * (100 - this->service_interval) / 100) :
00115 (this->date_of_last_service + this->service_interval >= _date)) {
00116 return false;
00117 }
00118
00119
00120
00121 if (!_settings_game.order.no_servicing_if_no_breakdowns ||
00122 _settings_game.difficulty.vehicle_breakdowns != 0) {
00123 return true;
00124 }
00125
00126
00127
00128
00129 bool pending_replace = false;
00130 Money needed_money = c->settings.engine_renew_money;
00131 if (needed_money > c->money) return false;
00132
00133 for (const Vehicle *v = this; v != NULL; v = (v->type == VEH_TRAIN) ? Train::From(v)->GetNextUnit() : NULL) {
00134 EngineID new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id);
00135
00136
00137 if (new_engine == INVALID_ENGINE || !HasBit(Engine::Get(new_engine)->company_avail, v->owner)) continue;
00138
00139
00140 uint32 available_cargo_types, union_mask;
00141 GetArticulatedRefitMasks(new_engine, true, &union_mask, &available_cargo_types);
00142
00143 if (union_mask != 0) {
00144 CargoID cargo_type;
00145
00146 if (IsArticulatedVehicleCarryingDifferentCargos(v, &cargo_type)) continue;
00147
00148
00149 if (cargo_type != CT_INVALID) {
00150
00151 if (!HasBit(available_cargo_types, cargo_type)) continue;
00152 }
00153 }
00154
00155
00156
00157 pending_replace = true;
00158 needed_money += 2 * Engine::Get(new_engine)->GetCost();
00159 if (needed_money > c->money) return false;
00160 }
00161
00162 return pending_replace;
00163 }
00164
00170 bool Vehicle::NeedsAutomaticServicing() const
00171 {
00172 if (_settings_game.order.gotodepot && this->HasDepotOrder()) return false;
00173 if (this->current_order.IsType(OT_LOADING)) return false;
00174 if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
00175 return NeedsServicing();
00176 }
00177
00178 uint Vehicle::Crash(bool flooded)
00179 {
00180 assert((this->vehstatus & VS_CRASHED) == 0);
00181 assert(this->Previous() == NULL);
00182
00183 uint pass = 0;
00184
00185 if (this->IsPrimaryVehicle()) this->vehstatus |= VS_STOPPED;
00186
00187 for (Vehicle *v = this; v != NULL; v = v->Next()) {
00188 if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo.Count();
00189 v->vehstatus |= VS_CRASHED;
00190 MarkSingleVehicleDirty(v);
00191 }
00192
00193
00194 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00195 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
00196 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
00197 SetWindowDirty(WC_VEHICLE_DEPOT, this->tile);
00198
00199 return pass;
00200 }
00201
00202
00211 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
00212 {
00213 const Engine *e = Engine::Get(engine);
00214 uint32 grfid = e->grf_prop.grffile->grfid;
00215 GRFConfig *grfconfig = GetGRFConfig(grfid);
00216
00217 if (!HasBit(grfconfig->grf_bugs, bug_type)) {
00218 SetBit(grfconfig->grf_bugs, bug_type);
00219 SetDParamStr(0, grfconfig->GetName());
00220 SetDParam(1, engine);
00221 ShowErrorMessage(part1, part2, WL_CRITICAL);
00222 if (!_networking) DoCommand(0, critical ? PM_PAUSED_ERROR : PM_PAUSED_NORMAL, 1, DC_EXEC, CMD_PAUSE);
00223 }
00224
00225
00226 char buffer[512];
00227
00228 SetDParamStr(0, grfconfig->GetName());
00229 GetString(buffer, part1, lastof(buffer));
00230 DEBUG(grf, 0, "%s", buffer + 3);
00231
00232 SetDParam(1, engine);
00233 GetString(buffer, part2, lastof(buffer));
00234 DEBUG(grf, 0, "%s", buffer + 3);
00235 }
00236
00241 Vehicle::Vehicle(VehicleType type)
00242 {
00243 this->type = type;
00244 this->coord.left = INVALID_COORD;
00245 this->group_id = DEFAULT_GROUP;
00246 this->fill_percent_te_id = INVALID_TE_ID;
00247 this->first = this;
00248 this->colourmap = PAL_NONE;
00249 }
00250
00255 byte VehicleRandomBits()
00256 {
00257 return GB(Random(), 0, 8);
00258 }
00259
00260
00261
00262 const int HASH_BITS = 7;
00263 const int HASH_SIZE = 1 << HASH_BITS;
00264 const int HASH_MASK = HASH_SIZE - 1;
00265 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
00266 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
00267
00268
00269
00270 const int HASH_RES = 0;
00271
00272 static Vehicle *_new_vehicle_position_hash[TOTAL_HASH_SIZE];
00273
00274 static Vehicle *VehicleFromHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
00275 {
00276 for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
00277 for (int x = xl; ; x = (x + 1) & HASH_MASK) {
00278 Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00279 for (; v != NULL; v = v->next_new_hash) {
00280 Vehicle *a = proc(v, data);
00281 if (find_first && a != NULL) return a;
00282 }
00283 if (x == xu) break;
00284 }
00285 if (y == yu) break;
00286 }
00287
00288 return NULL;
00289 }
00290
00291
00303 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
00304 {
00305 const int COLL_DIST = 6;
00306
00307
00308 int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00309 int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00310 int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00311 int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00312
00313 return VehicleFromHash(xl, yl, xu, yu, data, proc, find_first);
00314 }
00315
00330 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00331 {
00332 VehicleFromPosXY(x, y, data, proc, false);
00333 }
00334
00346 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00347 {
00348 return VehicleFromPosXY(x, y, data, proc, true) != NULL;
00349 }
00350
00361 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
00362 {
00363 int x = GB(TileX(tile), HASH_RES, HASH_BITS);
00364 int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
00365
00366 Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00367 for (; v != NULL; v = v->next_new_hash) {
00368 if (v->tile != tile) continue;
00369
00370 Vehicle *a = proc(v, data);
00371 if (find_first && a != NULL) return a;
00372 }
00373
00374 return NULL;
00375 }
00376
00390 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00391 {
00392 VehicleFromPos(tile, data, proc, false);
00393 }
00394
00405 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00406 {
00407 return VehicleFromPos(tile, data, proc, true) != NULL;
00408 }
00409
00416 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
00417 {
00418 byte z = *(byte*)data;
00419
00420 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
00421 if (v->z_pos > z) return NULL;
00422
00423 return v;
00424 }
00425
00431 CommandCost EnsureNoVehicleOnGround(TileIndex tile)
00432 {
00433 byte z = GetTileMaxZ(tile);
00434
00435
00436
00437
00438
00439 Vehicle *v = VehicleFromPos(tile, &z, &EnsureNoVehicleProcZ, true);
00440 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00441 return CommandCost();
00442 }
00443
00445 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
00446 {
00447 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
00448 if (v == (const Vehicle *)data) return NULL;
00449
00450 return v;
00451 }
00452
00460 CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
00461 {
00462
00463
00464
00465
00466 Vehicle *v = VehicleFromPos(tile, (void *)ignore, &GetVehicleTunnelBridgeProc, true);
00467 if (v == NULL) v = VehicleFromPos(endtile, (void *)ignore, &GetVehicleTunnelBridgeProc, true);
00468
00469 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00470 return CommandCost();
00471 }
00472
00473 static Vehicle *EnsureNoTrainOnTrackProc(Vehicle *v, void *data)
00474 {
00475 TrackBits rail_bits = *(TrackBits *)data;
00476
00477 if (v->type != VEH_TRAIN) return NULL;
00478
00479 Train *t = Train::From(v);
00480 if ((t->track != rail_bits) && !TracksOverlap(t->track | rail_bits)) return NULL;
00481
00482 return v;
00483 }
00484
00493 CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
00494 {
00495
00496
00497
00498
00499 Vehicle *v = VehicleFromPos(tile, &track_bits, &EnsureNoTrainOnTrackProc, true);
00500 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00501 return CommandCost();
00502 }
00503
00504 static void UpdateNewVehiclePosHash(Vehicle *v, bool remove)
00505 {
00506 Vehicle **old_hash = v->old_new_hash;
00507 Vehicle **new_hash;
00508
00509 if (remove) {
00510 new_hash = NULL;
00511 } else {
00512 int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
00513 int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
00514 new_hash = &_new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00515 }
00516
00517 if (old_hash == new_hash) return;
00518
00519
00520 if (old_hash != NULL) {
00521 if (v->next_new_hash != NULL) v->next_new_hash->prev_new_hash = v->prev_new_hash;
00522 *v->prev_new_hash = v->next_new_hash;
00523 }
00524
00525
00526 if (new_hash != NULL) {
00527 v->next_new_hash = *new_hash;
00528 if (v->next_new_hash != NULL) v->next_new_hash->prev_new_hash = &v->next_new_hash;
00529 v->prev_new_hash = new_hash;
00530 *new_hash = v;
00531 }
00532
00533
00534 v->old_new_hash = new_hash;
00535 }
00536
00537 static Vehicle *_vehicle_position_hash[0x1000];
00538
00539 static void UpdateVehiclePosHash(Vehicle *v, int x, int y)
00540 {
00541 UpdateNewVehiclePosHash(v, x == INVALID_COORD);
00542
00543 Vehicle **old_hash, **new_hash;
00544 int old_x = v->coord.left;
00545 int old_y = v->coord.top;
00546
00547 new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(x, y)];
00548 old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(old_x, old_y)];
00549
00550 if (old_hash == new_hash) return;
00551
00552
00553 if (old_hash != NULL) {
00554 if (v->next_hash != NULL) v->next_hash->prev_hash = v->prev_hash;
00555 *v->prev_hash = v->next_hash;
00556 }
00557
00558
00559 if (new_hash != NULL) {
00560 v->next_hash = *new_hash;
00561 if (v->next_hash != NULL) v->next_hash->prev_hash = &v->next_hash;
00562 v->prev_hash = new_hash;
00563 *new_hash = v;
00564 }
00565 }
00566
00567 void ResetVehiclePosHash()
00568 {
00569 Vehicle *v;
00570 FOR_ALL_VEHICLES(v) { v->old_new_hash = NULL; }
00571 memset(_vehicle_position_hash, 0, sizeof(_vehicle_position_hash));
00572 memset(_new_vehicle_position_hash, 0, sizeof(_new_vehicle_position_hash));
00573 }
00574
00575 void ResetVehicleColourMap()
00576 {
00577 Vehicle *v;
00578 FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
00579 }
00580
00585 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
00586 static AutoreplaceMap _vehicles_to_autoreplace;
00587
00588 void InitializeVehicles()
00589 {
00590 _vehicle_pool.CleanPool();
00591 _cargo_payment_pool.CleanPool();
00592
00593 _age_cargo_skip_counter = 1;
00594
00595 _vehicles_to_autoreplace.Reset();
00596 ResetVehiclePosHash();
00597 }
00598
00599 uint CountVehiclesInChain(const Vehicle *v)
00600 {
00601 uint count = 0;
00602 do count++; while ((v = v->Next()) != NULL);
00603 return count;
00604 }
00605
00611 void CountCompanyVehicles(CompanyID cid, uint counts[4])
00612 {
00613 for (uint i = 0; i < 4; i++) counts[i] = 0;
00614
00615 const Vehicle *v;
00616 FOR_ALL_VEHICLES(v) {
00617 if (v->owner == cid && v->IsPrimaryVehicle()) counts[v->type]++;
00618 }
00619 }
00620
00625 bool Vehicle::IsEngineCountable() const
00626 {
00627 switch (this->type) {
00628 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
00629 case VEH_TRAIN:
00630 return !Train::From(this)->IsArticulatedPart() &&
00631 !Train::From(this)->IsRearDualheaded();
00632 case VEH_ROAD: return RoadVehicle::From(this)->IsFrontEngine();
00633 case VEH_SHIP: return true;
00634 default: return false;
00635 }
00636 }
00637
00645 void Vehicle::HandlePathfindingResult(bool path_found)
00646 {
00647 if (path_found) {
00648
00649 if (!HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00650
00651
00652 ClrBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00653
00654 DeleteVehicleNews(this->index, STR_NEWS_VEHICLE_IS_LOST);
00655 return;
00656 }
00657
00658
00659 if (HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00660
00661
00662 SetBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00663
00664 AI::NewEvent(this->owner, new AIEventVehicleLost(this->index));
00665 if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) {
00666 SetDParam(0, this->index);
00667 AddVehicleNewsItem(STR_NEWS_VEHICLE_IS_LOST, NS_ADVICE, this->index);
00668 }
00669 }
00670
00672 void Vehicle::PreDestructor()
00673 {
00674 if (CleaningPool()) return;
00675
00676 if (Station::IsValidID(this->last_station_visited)) {
00677 Station::Get(this->last_station_visited)->loading_vehicles.remove(this);
00678
00679 HideFillingPercent(&this->fill_percent_te_id);
00680
00681 delete this->cargo_payment;
00682 }
00683
00684 if (this->IsEngineCountable()) {
00685 Company::Get(this->owner)->num_engines[this->engine_type]--;
00686 if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
00687
00688 DeleteGroupHighlightOfVehicle(this);
00689 if (Group::IsValidID(this->group_id)) Group::Get(this->group_id)->num_engines[this->engine_type]--;
00690 if (this->IsPrimaryVehicle()) DecreaseGroupNumVehicle(this->group_id);
00691 }
00692
00693 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
00694 Aircraft *a = Aircraft::From(this);
00695 Station *st = GetTargetAirportIfValid(a);
00696 if (st != NULL) {
00697 const AirportFTA *layout = st->airport.GetFTA()->layout;
00698 CLRBITS(st->airport.flags, layout[a->previous_pos].block | layout[a->pos].block);
00699 }
00700 }
00701
00702
00703 if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
00704 RoadVehicle *v = RoadVehicle::From(this);
00705 if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
00706
00707 RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
00708 }
00709 }
00710
00711 if (this->Previous() == NULL) {
00712 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
00713 }
00714
00715 if (this->IsPrimaryVehicle()) {
00716 DeleteWindowById(WC_VEHICLE_VIEW, this->index);
00717 DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
00718 DeleteWindowById(WC_VEHICLE_REFIT, this->index);
00719 DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
00720 DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
00721 SetWindowDirty(WC_COMPANY, this->owner);
00722 OrderBackup::ClearVehicle(this);
00723 }
00724 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00725
00726 this->cargo.Truncate(0);
00727 DeleteVehicleOrders(this);
00728 DeleteDepotHighlightOfVehicle(this);
00729
00730 extern void StopGlobalFollowVehicle(const Vehicle *v);
00731 StopGlobalFollowVehicle(this);
00732
00733 ReleaseDisastersTargetingVehicle(this->index);
00734 }
00735
00736 Vehicle::~Vehicle()
00737 {
00738 free(this->name);
00739
00740 if (CleaningPool()) return;
00741
00742
00743
00744 if (!(this->vehstatus & VS_HIDDEN)) MarkSingleVehicleDirty(this);
00745
00746 Vehicle *v = this->Next();
00747 this->SetNext(NULL);
00748
00749 delete v;
00750
00751 UpdateVehiclePosHash(this, INVALID_COORD, 0);
00752 DeleteVehicleNews(this->index, INVALID_STRING_ID);
00753 DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index);
00754 }
00755
00760 void VehicleEnteredDepotThisTick(Vehicle *v)
00761 {
00762
00763 _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
00764
00765
00766
00767
00768
00769
00770 v->vehstatus |= VS_STOPPED;
00771 }
00772
00778 static void RunVehicleDayProc()
00779 {
00780 if (_game_mode != GM_NORMAL) return;
00781
00782
00783 for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
00784 Vehicle *v = Vehicle::Get(i);
00785 if (v == NULL) continue;
00786
00787
00788 if ((v->day_counter & 0x1F) == 0) {
00789 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
00790 if (callback != CALLBACK_FAILED) {
00791 if (HasBit(callback, 0)) TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32);
00792 if (HasBit(callback, 1)) v->colourmap = PAL_NONE;
00793 }
00794 }
00795
00796
00797 v->OnNewDay();
00798 }
00799 }
00800
00801 void CallVehicleTicks()
00802 {
00803 _vehicles_to_autoreplace.Clear();
00804
00805 _age_cargo_skip_counter = (_age_cargo_skip_counter == 0) ? 184 : (_age_cargo_skip_counter - 1);
00806
00807 RunVehicleDayProc();
00808
00809 Station *st;
00810 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
00811
00812 Vehicle *v;
00813 FOR_ALL_VEHICLES(v) {
00814
00815 if (!v->Tick()) {
00816 assert(Vehicle::Get(vehicle_index) == NULL);
00817 continue;
00818 }
00819
00820 assert(Vehicle::Get(vehicle_index) == v);
00821
00822 switch (v->type) {
00823 default: break;
00824
00825 case VEH_TRAIN:
00826 case VEH_ROAD:
00827 case VEH_AIRCRAFT:
00828 case VEH_SHIP:
00829 if (_age_cargo_skip_counter == 0) v->cargo.AgeCargo();
00830
00831 if (v->type == VEH_TRAIN && Train::From(v)->IsWagon()) continue;
00832 if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
00833 if (v->type == VEH_ROAD && !RoadVehicle::From(v)->IsFrontEngine()) continue;
00834
00835 v->motion_counter += v->cur_speed;
00836
00837 if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
00838
00839
00840 if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
00841 }
00842 }
00843
00844 Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
00845 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
00846 v = it->first;
00847
00848 cur_company.Change(v->owner);
00849
00850
00851
00852
00853 if (it->second) v->vehstatus &= ~VS_STOPPED;
00854
00855
00856 int x = v->x_pos;
00857 int y = v->y_pos;
00858 int z = v->z_pos;
00859
00860 const Company *c = Company::Get(_current_company);
00861 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
00862 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
00863 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
00864
00865 if (!IsLocalCompany()) continue;
00866
00867 if (res.Succeeded()) {
00868 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
00869 continue;
00870 }
00871
00872 StringID error_message = res.GetErrorMessage();
00873 if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
00874
00875 if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
00876
00877 StringID message;
00878 if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
00879 message = error_message;
00880 } else {
00881 message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
00882 }
00883
00884 SetDParam(0, v->index);
00885 SetDParam(1, error_message);
00886 AddVehicleNewsItem(message, NS_ADVICE, v->index);
00887 }
00888
00889 cur_company.Restore();
00890 }
00891
00896 static void DoDrawVehicle(const Vehicle *v)
00897 {
00898 SpriteID image = v->cur_image;
00899 PaletteID pal = PAL_NONE;
00900
00901 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
00902
00903 AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
00904 v->x_extent, v->y_extent, v->z_extent, v->z_pos, (v->vehstatus & VS_SHADOW) != 0);
00905 }
00906
00907 void ViewportAddVehicles(DrawPixelInfo *dpi)
00908 {
00909
00910 const int l = dpi->left;
00911 const int r = dpi->left + dpi->width;
00912 const int t = dpi->top;
00913 const int b = dpi->top + dpi->height;
00914
00915
00916 int xl, xu, yl, yu;
00917
00918 if (dpi->width + 70 < (1 << (7 + 6))) {
00919 xl = GB(l - 70, 7, 6);
00920 xu = GB(r, 7, 6);
00921 } else {
00922
00923 xl = 0;
00924 xu = 0x3F;
00925 }
00926
00927 if (dpi->height + 70 < (1 << (6 + 6))) {
00928 yl = GB(t - 70, 6, 6) << 6;
00929 yu = GB(b, 6, 6) << 6;
00930 } else {
00931
00932 yl = 0;
00933 yu = 0x3F << 6;
00934 }
00935
00936 for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
00937 for (int x = xl;; x = (x + 1) & 0x3F) {
00938 const Vehicle *v = _vehicle_position_hash[x + y];
00939
00940 while (v != NULL) {
00941 if (!(v->vehstatus & VS_HIDDEN) &&
00942 l <= v->coord.right &&
00943 t <= v->coord.bottom &&
00944 r >= v->coord.left &&
00945 b >= v->coord.top) {
00946 DoDrawVehicle(v);
00947 }
00948 v = v->next_hash;
00949 }
00950
00951 if (x == xu) break;
00952 }
00953
00954 if (y == yu) break;
00955 }
00956 }
00957
00958 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
00959 {
00960 Vehicle *found = NULL, *v;
00961 uint dist, best_dist = UINT_MAX;
00962
00963 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
00964
00965 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
00966 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
00967
00968 FOR_ALL_VEHICLES(v) {
00969 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
00970 x >= v->coord.left && x <= v->coord.right &&
00971 y >= v->coord.top && y <= v->coord.bottom) {
00972
00973 dist = max(
00974 abs(((v->coord.left + v->coord.right) >> 1) - x),
00975 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
00976 );
00977
00978 if (dist < best_dist) {
00979 found = v;
00980 best_dist = dist;
00981 }
00982 }
00983 }
00984
00985 return found;
00986 }
00987
00988 void DecreaseVehicleValue(Vehicle *v)
00989 {
00990 v->value -= v->value >> 8;
00991 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
00992 }
00993
00994 static const byte _breakdown_chance[64] = {
00995 3, 3, 3, 3, 3, 3, 3, 3,
00996 4, 4, 5, 5, 6, 6, 7, 7,
00997 8, 8, 9, 9, 10, 10, 11, 11,
00998 12, 13, 13, 13, 13, 14, 15, 16,
00999 17, 19, 21, 25, 28, 31, 34, 37,
01000 40, 44, 48, 52, 56, 60, 64, 68,
01001 72, 80, 90, 100, 110, 120, 130, 140,
01002 150, 170, 190, 210, 230, 250, 250, 250,
01003 };
01004
01005 void CheckVehicleBreakdown(Vehicle *v)
01006 {
01007 int rel, rel_old;
01008
01009
01010 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
01011 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01012
01013 if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
01014 _settings_game.difficulty.vehicle_breakdowns < 1 ||
01015 v->cur_speed < 5 || _game_mode == GM_MENU) {
01016 return;
01017 }
01018
01019 uint32 r = Random();
01020
01021
01022 int chance = v->breakdown_chance + 1;
01023 if (Chance16I(1, 25, r)) chance += 25;
01024 v->breakdown_chance = min(255, chance);
01025
01026
01027 rel = v->reliability;
01028 if (v->type == VEH_SHIP) rel += 0x6666;
01029
01030
01031 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
01032
01033
01034 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
01035 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
01036 v->breakdown_delay = GB(r, 24, 7) + 0x80;
01037 v->breakdown_chance = 0;
01038 }
01039 }
01040
01047 bool Vehicle::HandleBreakdown()
01048 {
01049
01050
01051
01052
01053
01054 switch (this->breakdown_ctr) {
01055 case 0:
01056 return false;
01057
01058 case 2:
01059 this->breakdown_ctr = 1;
01060
01061 if (this->breakdowns_since_last_service != 255) {
01062 this->breakdowns_since_last_service++;
01063 }
01064
01065 this->MarkDirty();
01066 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01067 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01068
01069 if (this->type == VEH_AIRCRAFT) {
01070
01071 this->vehstatus |= VS_AIRCRAFT_BROKEN;
01072 } else {
01073 this->cur_speed = 0;
01074
01075 if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
01076 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
01077 (this->type == VEH_TRAIN ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
01078 (this->type == VEH_TRAIN ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
01079 }
01080
01081 if (!(this->vehstatus & VS_HIDDEN)) {
01082 EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
01083 if (u != NULL) u->animation_state = this->breakdown_delay * 2;
01084 }
01085 }
01086
01087 case 1:
01088
01089 if (this->type == VEH_AIRCRAFT) return false;
01090
01091
01092 if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
01093 if (--this->breakdown_delay == 0) {
01094 this->breakdown_ctr = 0;
01095 this->MarkDirty();
01096 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01097 }
01098 }
01099 return true;
01100
01101 default:
01102 if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
01103 return false;
01104 }
01105 }
01106
01111 void AgeVehicle(Vehicle *v)
01112 {
01113 if (v->age < MAX_DAY) v->age++;
01114
01115 int age = v->age - v->max_age;
01116 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
01117 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
01118 v->reliability_spd_dec <<= 1;
01119 }
01120
01121 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01122
01123
01124 if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
01125
01126
01127 if (Company::Get(v->owner)->settings.engine_renew && Engine::Get(v->engine_type)->company_avail != 0) return;
01128
01129 StringID str;
01130 if (age == -DAYS_IN_LEAP_YEAR) {
01131 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
01132 } else if (age == 0) {
01133 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
01134 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
01135 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
01136 } else {
01137 return;
01138 }
01139
01140 SetDParam(0, v->index);
01141 AddVehicleNewsItem(str, NS_ADVICE, v->index);
01142 }
01143
01150 uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *colour)
01151 {
01152 int count = 0;
01153 int max = 0;
01154 int cars = 0;
01155 int unloading = 0;
01156 bool loading = false;
01157
01158 const Vehicle *u = v;
01159
01160 const Station *st = Station::GetIfValid(v->last_station_visited);
01161 assert(colour == NULL || st != NULL);
01162
01163
01164 for (; v != NULL; v = v->Next()) {
01165 count += v->cargo.Count();
01166 max += v->cargo_cap;
01167 if (v->cargo_cap != 0 && colour != NULL) {
01168 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
01169 loading |= !(u->current_order.GetLoadType() & OLFB_NO_LOAD) && st->goods[v->cargo_type].days_since_pickup != 255;
01170 cars++;
01171 }
01172 }
01173
01174 if (colour != NULL) {
01175 if (unloading == 0 && loading) {
01176 *colour = STR_PERCENT_UP;
01177 } else if (cars == unloading || !loading) {
01178 *colour = STR_PERCENT_DOWN;
01179 } else {
01180 *colour = STR_PERCENT_UP_DOWN;
01181 }
01182 }
01183
01184
01185 if (max == 0) return 100;
01186
01187
01188 return (count * 100) / max;
01189 }
01190
01191 void VehicleEnterDepot(Vehicle *v)
01192 {
01193
01194 assert(v == v->First());
01195
01196 switch (v->type) {
01197 case VEH_TRAIN: {
01198 Train *t = Train::From(v);
01199 SetWindowClassesDirty(WC_TRAINS_LIST);
01200
01201 SetDepotReservation(t->tile, false);
01202 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
01203
01204 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
01205 t->wait_counter = 0;
01206 t->force_proceed = TFP_NONE;
01207 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
01208 t->ConsistChanged(true);
01209 break;
01210 }
01211
01212 case VEH_ROAD:
01213 SetWindowClassesDirty(WC_ROADVEH_LIST);
01214 break;
01215
01216 case VEH_SHIP: {
01217 SetWindowClassesDirty(WC_SHIPS_LIST);
01218 Ship *ship = Ship::From(v);
01219 ship->state = TRACK_BIT_DEPOT;
01220 ship->UpdateCache();
01221 ship->UpdateViewport(true, true);
01222 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01223 break;
01224 }
01225
01226 case VEH_AIRCRAFT:
01227 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01228 HandleAircraftEnterHangar(Aircraft::From(v));
01229 break;
01230 default: NOT_REACHED();
01231 }
01232 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01233
01234 if (v->type != VEH_TRAIN) {
01235
01236
01237 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01238 }
01239 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01240
01241 v->vehstatus |= VS_HIDDEN;
01242 v->cur_speed = 0;
01243
01244 VehicleServiceInDepot(v);
01245
01246 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01247
01248 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01249 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01250
01251 const Order *real_order = v->GetNextManualOrder(v->cur_order_index);
01252 Order t = v->current_order;
01253 v->current_order.MakeDummy();
01254
01255
01256
01257 if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
01258 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
01259 (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
01260
01261 return;
01262 }
01263
01264 if (t.IsRefit()) {
01265 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01266 CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
01267 cur_company.Restore();
01268
01269 if (cost.Failed()) {
01270 _vehicles_to_autoreplace[v] = false;
01271 if (v->owner == _local_company) {
01272
01273 SetDParam(0, v->index);
01274 AddVehicleNewsItem(STR_NEWS_ORDER_REFIT_FAILED, NS_ADVICE, v->index);
01275 }
01276 } else if (cost.GetCost() != 0) {
01277 v->profit_this_year -= cost.GetCost() << 8;
01278 if (v->owner == _local_company) {
01279 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01280 }
01281 }
01282 }
01283
01284 if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01285
01286 v->DeleteUnreachedAutoOrders();
01287 UpdateVehicleTimetable(v, true);
01288 v->IncrementOrderIndex();
01289 }
01290 if (t.GetDepotActionType() & ODATFB_HALT) {
01291
01292 _vehicles_to_autoreplace[v] = false;
01293 if (v->owner == _local_company) {
01294 SetDParam(0, v->index);
01295 AddVehicleNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, NS_ADVICE, v->index);
01296 }
01297 AI::NewEvent(v->owner, new AIEventVehicleWaitingInDepot(v->index));
01298 }
01299 }
01300 }
01301
01302
01310 void VehicleMove(Vehicle *v, bool update_viewport)
01311 {
01312 int img = v->cur_image;
01313 Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
01314 const Sprite *spr = GetSprite(img, ST_NORMAL);
01315
01316 pt.x += spr->x_offs;
01317 pt.y += spr->y_offs;
01318
01319 UpdateVehiclePosHash(v, pt.x, pt.y);
01320
01321 Rect old_coord = v->coord;
01322 v->coord.left = pt.x;
01323 v->coord.top = pt.y;
01324 v->coord.right = pt.x + spr->width + 2;
01325 v->coord.bottom = pt.y + spr->height + 2;
01326
01327 if (update_viewport) {
01328 MarkAllViewportsDirty(
01329 min(old_coord.left, v->coord.left),
01330 min(old_coord.top, v->coord.top),
01331 max(old_coord.right, v->coord.right) + 1,
01332 max(old_coord.bottom, v->coord.bottom) + 1
01333 );
01334 }
01335 }
01336
01345 void MarkSingleVehicleDirty(const Vehicle *v)
01346 {
01347 MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1, v->coord.bottom + 1);
01348 }
01349
01355 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01356 {
01357 static const int8 _delta_coord[16] = {
01358 -1,-1,-1, 0, 1, 1, 1, 0,
01359 -1, 0, 1, 1, 1, 0,-1,-1,
01360 };
01361
01362 int x = v->x_pos + _delta_coord[v->direction];
01363 int y = v->y_pos + _delta_coord[v->direction + 8];
01364
01365 GetNewVehiclePosResult gp;
01366 gp.x = x;
01367 gp.y = y;
01368 gp.old_tile = v->tile;
01369 gp.new_tile = TileVirtXY(x, y);
01370 return gp;
01371 }
01372
01373 static const Direction _new_direction_table[] = {
01374 DIR_N, DIR_NW, DIR_W,
01375 DIR_NE, DIR_SE, DIR_SW,
01376 DIR_E, DIR_SE, DIR_S
01377 };
01378
01379 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01380 {
01381 int i = 0;
01382
01383 if (y >= v->y_pos) {
01384 if (y != v->y_pos) i += 3;
01385 i += 3;
01386 }
01387
01388 if (x >= v->x_pos) {
01389 if (x != v->x_pos) i++;
01390 i++;
01391 }
01392
01393 Direction dir = v->direction;
01394
01395 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01396 if (dirdiff == DIRDIFF_SAME) return dir;
01397 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01398 }
01399
01409 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01410 {
01411 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01412 }
01413
01421 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01422 {
01423
01424 const Vehicle *v;
01425 FOR_ALL_VEHICLES(v) {
01426 if (v->type == type && v->owner == owner) {
01427 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01428 }
01429 }
01430
01431 if (this->maxid == 0) return;
01432
01433
01434
01435
01436 this->cache = CallocT<bool>(this->maxid + 2);
01437
01438
01439 FOR_ALL_VEHICLES(v) {
01440 if (v->type == type && v->owner == owner) {
01441 this->cache[v->unitnumber] = true;
01442 }
01443 }
01444 }
01445
01447 UnitID FreeUnitIDGenerator::NextID()
01448 {
01449 if (this->maxid <= this->curid) return ++this->curid;
01450
01451 while (this->cache[++this->curid]) { }
01452
01453 return this->curid;
01454 }
01455
01461 UnitID GetFreeUnitNumber(VehicleType type)
01462 {
01463
01464 uint max_veh;
01465 switch (type) {
01466 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
01467 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
01468 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
01469 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
01470 default: NOT_REACHED();
01471 }
01472
01473 uint amounts[4];
01474 CountCompanyVehicles(_current_company, amounts);
01475 assert((uint)type < lengthof(amounts));
01476 if (amounts[type] >= max_veh) return UINT16_MAX;
01477
01478 FreeUnitIDGenerator gen(type, _current_company);
01479
01480 return gen.NextID();
01481 }
01482
01483
01492 bool CanBuildVehicleInfrastructure(VehicleType type)
01493 {
01494 assert(IsCompanyBuildableVehicleType(type));
01495
01496 if (!Company::IsValidID(_local_company)) return false;
01497 if (!_settings_client.gui.disable_unsuitable_building) return true;
01498
01499 UnitID max;
01500 switch (type) {
01501 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
01502 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
01503 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
01504 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01505 default: NOT_REACHED();
01506 }
01507
01508
01509 if (max > 0) {
01510
01511 const Engine *e;
01512 FOR_ALL_ENGINES_OF_TYPE(e, type) {
01513 if (HasBit(e->company_avail, _local_company)) return true;
01514 }
01515 return false;
01516 }
01517
01518
01519 const Vehicle *v;
01520 FOR_ALL_VEHICLES(v) {
01521 if (v->owner == _local_company && v->type == type) return true;
01522 }
01523
01524 return false;
01525 }
01526
01527
01535 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
01536 {
01537 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01538 const Engine *e = Engine::Get(engine_type);
01539 switch (e->type) {
01540 default: NOT_REACHED();
01541 case VEH_TRAIN:
01542 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (Train::From(v)->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
01543
01544
01545 engine_type = parent_engine_type;
01546 e = Engine::Get(engine_type);
01547
01548 }
01549
01550 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01551 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01552 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
01553 if (!CargoSpec::Get(cargo_type)->is_freight) {
01554 if (parent_engine_type == INVALID_ENGINE) {
01555 return LS_PASSENGER_WAGON_STEAM;
01556 } else {
01557 switch (RailVehInfo(parent_engine_type)->engclass) {
01558 default: NOT_REACHED();
01559 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
01560 case EC_DIESEL: return LS_PASSENGER_WAGON_DIESEL;
01561 case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
01562 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
01563 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
01564 }
01565 }
01566 } else {
01567 return LS_FREIGHT_WAGON;
01568 }
01569 } else {
01570 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
01571
01572 switch (e->u.rail.engclass) {
01573 default: NOT_REACHED();
01574 case EC_STEAM: return LS_STEAM;
01575 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
01576 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
01577 case EC_MONORAIL: return LS_MONORAIL;
01578 case EC_MAGLEV: return LS_MAGLEV;
01579 }
01580 }
01581
01582 case VEH_ROAD:
01583
01584 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01585 engine_type = parent_engine_type;
01586 e = Engine::Get(engine_type);
01587 cargo_type = v->First()->cargo_type;
01588 }
01589 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01590 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01591
01592
01593 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
01594
01595 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01596 } else {
01597
01598 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01599 }
01600
01601 case VEH_SHIP:
01602 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01603 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01604 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01605
01606 case VEH_AIRCRAFT:
01607 switch (e->u.air.subtype) {
01608 case AIR_HELI: return LS_HELICOPTER;
01609 case AIR_CTOL: return LS_SMALL_PLANE;
01610 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
01611 default: NOT_REACHED();
01612 }
01613 }
01614 }
01615
01625 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
01626 {
01627 const Company *c = Company::Get(company);
01628 LiveryScheme scheme = LS_DEFAULT;
01629
01630
01631
01632 if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
01633
01634 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
01635
01636
01637 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01638 }
01639
01640 return &c->livery[scheme];
01641 }
01642
01643
01644 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01645 {
01646 PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01647
01648
01649 if (map != PAL_NONE) return map;
01650
01651 const Engine *e = Engine::Get(engine_type);
01652
01653
01654 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
01655 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01656
01657 if (callback != CALLBACK_FAILED) {
01658 assert_compile(PAL_NONE == 0);
01659 map = GB(callback, 0, 14);
01660
01661
01662 if (!HasBit(callback, 14)) {
01663
01664 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01665 return map;
01666 }
01667 }
01668 }
01669
01670 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
01671
01672 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
01673
01674
01675 if (!Company::IsValidID(company)) return map;
01676
01677 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
01678
01679 map += livery->colour1;
01680 if (twocc) map += livery->colour2 * 16;
01681
01682
01683 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01684 return map;
01685 }
01686
01693 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
01694 {
01695 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01696 }
01697
01703 PaletteID GetVehiclePalette(const Vehicle *v)
01704 {
01705 if (v->IsGroundVehicle()) {
01706 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
01707 }
01708
01709 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01710 }
01711
01720 uint GetVehicleCapacity(const Vehicle *v, uint16 *mail_capacity)
01721 {
01722 if (mail_capacity != NULL) *mail_capacity = 0;
01723 const Engine *e = Engine::Get(v->engine_type);
01724
01725 if (!e->CanCarryCargo()) return 0;
01726
01727 if (mail_capacity != NULL && e->type == VEH_AIRCRAFT && IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
01728 *mail_capacity = GetVehicleProperty(v, PROP_AIRCRAFT_MAIL_CAPACITY, e->u.air.mail_capacity);
01729 }
01730 CargoID default_cargo = e->GetDefaultCargoType();
01731
01732
01733
01734 if (HasBit(e->info.callback_mask, CBM_VEHICLE_REFIT_CAPACITY) &&
01735 (default_cargo != v->cargo_type || v->cargo_subtype != 0)) {
01736 uint16 callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
01737 if (callback != CALLBACK_FAILED) return callback;
01738 }
01739
01740
01741 uint capacity;
01742 switch (e->type) {
01743 case VEH_TRAIN: capacity = GetVehicleProperty(v, PROP_TRAIN_CARGO_CAPACITY, e->u.rail.capacity); break;
01744 case VEH_ROAD: capacity = GetVehicleProperty(v, PROP_ROADVEH_CARGO_CAPACITY, e->u.road.capacity); break;
01745 case VEH_SHIP: capacity = GetVehicleProperty(v, PROP_SHIP_CARGO_CAPACITY, e->u.ship.capacity); break;
01746 case VEH_AIRCRAFT: capacity = GetVehicleProperty(v, PROP_AIRCRAFT_PASSENGER_CAPACITY, e->u.air.passenger_capacity); break;
01747 default: NOT_REACHED();
01748 }
01749
01750
01751
01752 if (e->type != VEH_SHIP) {
01753 if (e->type == VEH_AIRCRAFT) {
01754 if (!IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
01755 capacity += GetVehicleProperty(v, PROP_AIRCRAFT_MAIL_CAPACITY, e->u.air.mail_capacity);
01756 }
01757 if (v->cargo_type == CT_MAIL) return capacity;
01758 } else {
01759 switch (default_cargo) {
01760 case CT_PASSENGERS: break;
01761 case CT_MAIL:
01762 case CT_GOODS: capacity *= 2; break;
01763 default: capacity *= 4; break;
01764 }
01765 }
01766 switch (v->cargo_type) {
01767 case CT_PASSENGERS: break;
01768 case CT_MAIL:
01769 case CT_GOODS: capacity /= 2; break;
01770 default: capacity /= 4; break;
01771 }
01772 }
01773
01774 return capacity;
01775 }
01776
01780 void Vehicle::DeleteUnreachedAutoOrders()
01781 {
01782 const Order *order = this->GetOrder(this->cur_order_index);
01783 while (order != NULL && order->IsType(OT_AUTOMATIC)) {
01784
01785 order = order->next;
01786 DeleteOrder(this, this->cur_order_index);
01787 }
01788 }
01789
01790 void Vehicle::BeginLoading()
01791 {
01792 assert(IsTileType(tile, MP_STATION) || type == VEH_SHIP);
01793
01794 if (this->current_order.IsType(OT_GOTO_STATION) &&
01795 this->current_order.GetDestination() == this->last_station_visited) {
01796 this->DeleteUnreachedAutoOrders();
01797
01798
01799 this->current_order.MakeLoading(true);
01800 UpdateVehicleTimetable(this, true);
01801
01802
01803
01804
01805
01806
01807 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
01808
01809 } else {
01810
01811
01812
01813 Order *in_list = this->GetOrder(this->cur_order_index);
01814 if (in_list != NULL && this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID &&
01815 (!in_list->IsType(OT_AUTOMATIC) ||
01816 in_list->GetDestination() != this->last_station_visited)) {
01817 Order *auto_order = new Order();
01818 auto_order->MakeAutomatic(this->last_station_visited);
01819 InsertOrder(this, auto_order, this->cur_order_index);
01820 if (this->cur_order_index > 0) --this->cur_order_index;
01821 }
01822 this->current_order.MakeLoading(false);
01823 }
01824
01825 Station::Get(this->last_station_visited)->loading_vehicles.push_back(this);
01826
01827 PrepareUnload(this);
01828
01829 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
01830 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01831 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01832 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
01833
01834 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
01835 this->cur_speed = 0;
01836 this->MarkDirty();
01837 }
01838
01839 void Vehicle::LeaveStation()
01840 {
01841 assert(current_order.IsType(OT_LOADING));
01842
01843 delete this->cargo_payment;
01844
01845
01846 if (current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
01847
01848 current_order.MakeLeaveStation();
01849 Station *st = Station::Get(this->last_station_visited);
01850 st->loading_vehicles.remove(this);
01851
01852 HideFillingPercent(&this->fill_percent_te_id);
01853
01854 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
01855
01856 if (IsTileType(this->tile, MP_STATION)) TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
01857
01858 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
01859 }
01860 }
01861
01862
01868 void Vehicle::HandleLoading(bool mode)
01869 {
01870 switch (this->current_order.GetType()) {
01871 case OT_LOADING: {
01872 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
01873
01874
01875 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) ||
01876 (_settings_game.order.timetabling && this->current_order_time < wait_time)) return;
01877
01878 this->PlayLeaveStationSound();
01879
01880 this->LeaveStation();
01881
01882 break;
01883 }
01884
01885 case OT_DUMMY: break;
01886
01887 default: return;
01888 }
01889
01890 this->IncrementOrderIndex();
01891 }
01892
01899 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
01900 {
01901 CommandCost ret = CheckOwnership(this->owner);
01902 if (ret.Failed()) return ret;
01903
01904 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
01905 if (this->IsStoppedInDepot()) return CMD_ERROR;
01906
01907 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
01908 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
01909 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
01910
01911
01912
01913 if (flags & DC_EXEC) {
01914 this->current_order.SetDepotOrderType(ODTF_MANUAL);
01915 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
01916 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01917 }
01918 return CommandCost();
01919 }
01920
01921 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR;
01922 if (flags & DC_EXEC) {
01923
01924
01925 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementOrderIndex();
01926
01927 this->current_order.MakeDummy();
01928 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01929 }
01930 return CommandCost();
01931 }
01932
01933 TileIndex location;
01934 DestinationID destination;
01935 bool reverse;
01936 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};
01937 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
01938
01939 if (flags & DC_EXEC) {
01940 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
01941
01942 this->dest_tile = location;
01943 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
01944 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
01945 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01946
01947
01948 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
01949
01950 if (this->type == VEH_AIRCRAFT) {
01951 Aircraft *a = Aircraft::From(this);
01952 if (a->state == FLYING && a->targetairport != destination) {
01953
01954 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
01955 AircraftNextAirportPos_and_Order(a);
01956 }
01957 }
01958 }
01959
01960 return CommandCost();
01961
01962 }
01963
01968 void Vehicle::UpdateVisualEffect(bool allow_power_change)
01969 {
01970 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
01971 const Engine *e = Engine::Get(this->engine_type);
01972
01973
01974 byte visual_effect;
01975 switch (e->type) {
01976 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
01977 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
01978 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
01979 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
01980 }
01981
01982
01983 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
01984 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
01985
01986 if (callback != CALLBACK_FAILED) {
01987 callback = GB(callback, 0, 8);
01988
01989
01990 if (callback == VE_DEFAULT) {
01991 assert(HasBit(callback, VE_DISABLE_EFFECT));
01992 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
01993 }
01994 visual_effect = callback;
01995 }
01996 }
01997
01998
01999 if (visual_effect == VE_DEFAULT ||
02000 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
02001
02002
02003 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
02004 if (visual_effect == VE_DEFAULT) {
02005 visual_effect = 1 << VE_DISABLE_EFFECT;
02006 } else {
02007 SetBit(visual_effect, VE_DISABLE_EFFECT);
02008 }
02009 } else {
02010 if (visual_effect == VE_DEFAULT) {
02011
02012 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
02013 }
02014 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
02015 }
02016 }
02017
02018 this->vcache.cached_vis_effect = visual_effect;
02019
02020 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
02021 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02022 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
02023 }
02024 }
02025
02026 static const int8 _vehicle_smoke_pos[8] = {
02027 1, 1, 1, 0, -1, -1, -1, 0
02028 };
02029
02034 void Vehicle::ShowVisualEffect() const
02035 {
02036 assert(this->IsPrimaryVehicle());
02037 bool sound = false;
02038
02039
02040
02041
02042
02043
02044 if (_settings_game.vehicle.smoke_amount == 0 ||
02045 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
02046 this->cur_speed < 2) {
02047 return;
02048 }
02049 if (this->type == VEH_TRAIN) {
02050 const Train *t = Train::From(this);
02051
02052
02053
02054
02055 if (HasBit(t->flags, VRF_REVERSING) ||
02056 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
02057 t->cur_speed >= t->Train::GetCurrentMaxSpeed())) {
02058 return;
02059 }
02060 }
02061
02062 const Vehicle *v = this;
02063
02064 do {
02065 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
02066 byte effect_type = GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
02067 bool disable_effect = HasBit(v->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
02068
02069
02070
02071
02072
02073
02074
02075 if (disable_effect ||
02076 v->vehstatus & VS_HIDDEN ||
02077 IsDepotTile(v->tile) ||
02078 IsTunnelTile(v->tile) ||
02079 (v->type == VEH_TRAIN &&
02080 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
02081 continue;
02082 }
02083
02084 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
02085 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
02086
02087 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
02088 x = -x;
02089 y = -y;
02090 }
02091
02092 switch (effect_type) {
02093 case VE_TYPE_STEAM:
02094
02095
02096
02097
02098
02099 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / this->vcache.cached_max_speed))) == 0) {
02100 CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
02101 sound = true;
02102 }
02103 break;
02104
02105 case VE_TYPE_DIESEL: {
02106
02107
02108
02109
02110
02111
02112
02113
02114
02115
02116
02117 int power_weight_effect = 0;
02118 if (v->type == VEH_TRAIN) {
02119 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
02120 }
02121 if (this->cur_speed < (this->vcache.cached_max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
02122 Chance16((64 - ((this->cur_speed << 5) / this->vcache.cached_max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
02123 CreateEffectVehicleRel(v, x, y, 10, EV_DIESEL_SMOKE);
02124 sound = true;
02125 }
02126 break;
02127 }
02128
02129 case VE_TYPE_ELECTRIC:
02130
02131
02132
02133
02134
02135
02136 if (GB(v->tick_counter, 0, 2) == 0 &&
02137 Chance16((6 - ((this->cur_speed << 2) / this->vcache.cached_max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
02138 CreateEffectVehicleRel(v, x, y, 10, EV_ELECTRIC_SPARK);
02139 sound = true;
02140 }
02141 break;
02142
02143 default:
02144 break;
02145 }
02146 } while ((v = v->Next()) != NULL);
02147
02148 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
02149 }
02150
02155 void Vehicle::SetNext(Vehicle *next)
02156 {
02157 assert(this != next);
02158
02159 if (this->next != NULL) {
02160
02161 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02162 v->first = this->next;
02163 }
02164 this->next->previous = NULL;
02165 }
02166
02167 this->next = next;
02168
02169 if (this->next != NULL) {
02170
02171 if (this->next->previous != NULL) this->next->previous->next = NULL;
02172 this->next->previous = this;
02173 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02174 v->first = this->first;
02175 }
02176 }
02177 }
02178
02184 void Vehicle::AddToShared(Vehicle *shared_chain)
02185 {
02186 assert(this->previous_shared == NULL && this->next_shared == NULL);
02187
02188 if (!shared_chain->orders.list) {
02189 assert(shared_chain->previous_shared == NULL);
02190 assert(shared_chain->next_shared == NULL);
02191 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
02192 }
02193
02194 this->next_shared = shared_chain->next_shared;
02195 this->previous_shared = shared_chain;
02196
02197 shared_chain->next_shared = this;
02198
02199 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
02200
02201 shared_chain->orders.list->AddVehicle(this);
02202 }
02203
02207 void Vehicle::RemoveFromShared()
02208 {
02209
02210
02211 bool were_first = (this->FirstShared() == this);
02212 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
02213
02214 this->orders.list->RemoveVehicle(this);
02215
02216 if (!were_first) {
02217
02218 this->previous_shared->next_shared = this->NextShared();
02219 }
02220
02221 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
02222
02223
02224 if (this->orders.list->GetNumVehicles() == 1) {
02225
02226 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
02227 InvalidateVehicleOrder(this->FirstShared(), 0);
02228 } else if (were_first) {
02229
02230
02231 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
02232 }
02233
02234 this->next_shared = NULL;
02235 this->previous_shared = NULL;
02236 }
02237
02243 Order *Vehicle::GetNextManualOrder(int index) const
02244 {
02245 Order *order = this->GetOrder(index);
02246 while(order != NULL && order->IsType(OT_AUTOMATIC)) {
02247 order = order->next;
02248 }
02249 return order;
02250 }
02251
02252 void VehiclesYearlyLoop()
02253 {
02254 Vehicle *v;
02255 FOR_ALL_VEHICLES(v) {
02256 if (v->IsPrimaryVehicle()) {
02257
02258 Money profit = v->GetDisplayProfitThisYear();
02259 if (v->age >= 730 && profit < 0) {
02260 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
02261 SetDParam(0, v->index);
02262 SetDParam(1, profit);
02263 AddVehicleNewsItem(
02264 STR_NEWS_VEHICLE_IS_UNPROFITABLE,
02265 NS_ADVICE,
02266 v->index
02267 );
02268 }
02269 AI::NewEvent(v->owner, new AIEventVehicleUnprofitable(v->index));
02270 }
02271
02272 v->profit_last_year = v->profit_this_year;
02273 v->profit_this_year = 0;
02274 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
02275 }
02276 }
02277 }
02278
02279
02289 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
02290 {
02291 const Engine *e = Engine::GetIfValid(engine_type);
02292 assert(e != NULL);
02293
02294 switch (e->type) {
02295 case VEH_TRAIN:
02296 return (st->facilities & FACIL_TRAIN) != 0;
02297
02298 case VEH_ROAD:
02299
02300
02301
02302 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
02303
02304 case VEH_SHIP:
02305 return (st->facilities & FACIL_DOCK) != 0;
02306
02307 case VEH_AIRCRAFT:
02308 return (st->facilities & FACIL_AIRPORT) != 0 &&
02309 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
02310
02311 default:
02312 return false;
02313 }
02314 }
02315
02322 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
02323 {
02324 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
02325
02326 return CanVehicleUseStation(v->engine_type, st);
02327 }
02328
02334 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
02335 {
02336 assert(this->IsGroundVehicle());
02337 if (this->type == VEH_TRAIN) {
02338 return &Train::From(this)->gcache;
02339 } else {
02340 return &RoadVehicle::From(this)->gcache;
02341 }
02342 }
02343
02349 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
02350 {
02351 assert(this->IsGroundVehicle());
02352 if (this->type == VEH_TRAIN) {
02353 return &Train::From(this)->gcache;
02354 } else {
02355 return &RoadVehicle::From(this)->gcache;
02356 }
02357 }
02358
02367 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
02368 {
02369 if (v->type == VEH_TRAIN) {
02370 Train *u = Train::From(v);
02371
02372 if (u->IsArticulatedPart()) {
02373 u = u->GetFirstEnginePart();
02374 while (u->index != v->index) {
02375 set.Include(u->index);
02376 u = u->GetNextArticulatedPart();
02377 }
02378 }
02379
02380 for (;u != NULL && num_vehicles > 0; num_vehicles--, u = u->Next()) {
02381
02382 set.Include(u->index);
02383
02384
02385 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
02386 }
02387
02388
02389 while (u != NULL && u->IsArticulatedPart()) {
02390 set.Include(u->index);
02391 u = u->Next();
02392 }
02393 }
02394 }