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 #include "cargodest_func.h"
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
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 (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 this->last_station_loaded = INVALID_STATION;
00250 this->current_order.index = INVALID_ORDER;
00251 this->last_order_id = INVALID_ORDER;
00252 }
00253
00258 byte VehicleRandomBits()
00259 {
00260 return GB(Random(), 0, 8);
00261 }
00262
00263
00264
00265 const int HASH_BITS = 7;
00266 const int HASH_SIZE = 1 << HASH_BITS;
00267 const int HASH_MASK = HASH_SIZE - 1;
00268 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
00269 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
00270
00271
00272
00273 const int HASH_RES = 0;
00274
00275 static Vehicle *_new_vehicle_position_hash[TOTAL_HASH_SIZE];
00276
00277 static Vehicle *VehicleFromHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
00278 {
00279 for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
00280 for (int x = xl; ; x = (x + 1) & HASH_MASK) {
00281 Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00282 for (; v != NULL; v = v->next_new_hash) {
00283 Vehicle *a = proc(v, data);
00284 if (find_first && a != NULL) return a;
00285 }
00286 if (x == xu) break;
00287 }
00288 if (y == yu) break;
00289 }
00290
00291 return NULL;
00292 }
00293
00294
00306 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
00307 {
00308 const int COLL_DIST = 6;
00309
00310
00311 int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00312 int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00313 int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00314 int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00315
00316 return VehicleFromHash(xl, yl, xu, yu, data, proc, find_first);
00317 }
00318
00333 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00334 {
00335 VehicleFromPosXY(x, y, data, proc, false);
00336 }
00337
00349 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00350 {
00351 return VehicleFromPosXY(x, y, data, proc, true) != NULL;
00352 }
00353
00364 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
00365 {
00366 int x = GB(TileX(tile), HASH_RES, HASH_BITS);
00367 int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
00368
00369 Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00370 for (; v != NULL; v = v->next_new_hash) {
00371 if (v->tile != tile) continue;
00372
00373 Vehicle *a = proc(v, data);
00374 if (find_first && a != NULL) return a;
00375 }
00376
00377 return NULL;
00378 }
00379
00393 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00394 {
00395 VehicleFromPos(tile, data, proc, false);
00396 }
00397
00408 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00409 {
00410 return VehicleFromPos(tile, data, proc, true) != NULL;
00411 }
00412
00419 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
00420 {
00421 byte z = *(byte*)data;
00422
00423 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
00424 if (v->z_pos > z) return NULL;
00425
00426 return v;
00427 }
00428
00434 CommandCost EnsureNoVehicleOnGround(TileIndex tile)
00435 {
00436 byte z = GetTileMaxZ(tile);
00437
00438
00439
00440
00441
00442 Vehicle *v = VehicleFromPos(tile, &z, &EnsureNoVehicleProcZ, true);
00443 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00444 return CommandCost();
00445 }
00446
00448 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
00449 {
00450 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
00451 if (v == (const Vehicle *)data) return NULL;
00452
00453 return v;
00454 }
00455
00463 CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
00464 {
00465
00466
00467
00468
00469 Vehicle *v = VehicleFromPos(tile, (void *)ignore, &GetVehicleTunnelBridgeProc, true);
00470 if (v == NULL) v = VehicleFromPos(endtile, (void *)ignore, &GetVehicleTunnelBridgeProc, true);
00471
00472 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00473 return CommandCost();
00474 }
00475
00476 static Vehicle *EnsureNoTrainOnTrackProc(Vehicle *v, void *data)
00477 {
00478 TrackBits rail_bits = *(TrackBits *)data;
00479
00480 if (v->type != VEH_TRAIN) return NULL;
00481
00482 Train *t = Train::From(v);
00483 if ((t->track != rail_bits) && !TracksOverlap(t->track | rail_bits)) return NULL;
00484
00485 return v;
00486 }
00487
00496 CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
00497 {
00498
00499
00500
00501
00502 Vehicle *v = VehicleFromPos(tile, &track_bits, &EnsureNoTrainOnTrackProc, true);
00503 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00504 return CommandCost();
00505 }
00506
00507 static void UpdateNewVehiclePosHash(Vehicle *v, bool remove)
00508 {
00509 Vehicle **old_hash = v->old_new_hash;
00510 Vehicle **new_hash;
00511
00512 if (remove) {
00513 new_hash = NULL;
00514 } else {
00515 int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
00516 int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
00517 new_hash = &_new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00518 }
00519
00520 if (old_hash == new_hash) return;
00521
00522
00523 if (old_hash != NULL) {
00524 if (v->next_new_hash != NULL) v->next_new_hash->prev_new_hash = v->prev_new_hash;
00525 *v->prev_new_hash = v->next_new_hash;
00526 }
00527
00528
00529 if (new_hash != NULL) {
00530 v->next_new_hash = *new_hash;
00531 if (v->next_new_hash != NULL) v->next_new_hash->prev_new_hash = &v->next_new_hash;
00532 v->prev_new_hash = new_hash;
00533 *new_hash = v;
00534 }
00535
00536
00537 v->old_new_hash = new_hash;
00538 }
00539
00540 static Vehicle *_vehicle_position_hash[0x1000];
00541
00542 static void UpdateVehiclePosHash(Vehicle *v, int x, int y)
00543 {
00544 UpdateNewVehiclePosHash(v, x == INVALID_COORD);
00545
00546 Vehicle **old_hash, **new_hash;
00547 int old_x = v->coord.left;
00548 int old_y = v->coord.top;
00549
00550 new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(x, y)];
00551 old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(old_x, old_y)];
00552
00553 if (old_hash == new_hash) return;
00554
00555
00556 if (old_hash != NULL) {
00557 if (v->next_hash != NULL) v->next_hash->prev_hash = v->prev_hash;
00558 *v->prev_hash = v->next_hash;
00559 }
00560
00561
00562 if (new_hash != NULL) {
00563 v->next_hash = *new_hash;
00564 if (v->next_hash != NULL) v->next_hash->prev_hash = &v->next_hash;
00565 v->prev_hash = new_hash;
00566 *new_hash = v;
00567 }
00568 }
00569
00570 void ResetVehiclePosHash()
00571 {
00572 Vehicle *v;
00573 FOR_ALL_VEHICLES(v) { v->old_new_hash = NULL; }
00574 memset(_vehicle_position_hash, 0, sizeof(_vehicle_position_hash));
00575 memset(_new_vehicle_position_hash, 0, sizeof(_new_vehicle_position_hash));
00576 }
00577
00578 void ResetVehicleColourMap()
00579 {
00580 Vehicle *v;
00581 FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
00582 }
00583
00588 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
00589 static AutoreplaceMap _vehicles_to_autoreplace;
00590
00591 void InitializeVehicles()
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 !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()) {
00741 this->cargo.OnCleanPool();
00742 return;
00743 }
00744
00745
00746
00747 if (!(this->vehstatus & VS_HIDDEN)) MarkSingleVehicleDirty(this);
00748
00749 Vehicle *v = this->Next();
00750 this->SetNext(NULL);
00751
00752 delete v;
00753
00754 UpdateVehiclePosHash(this, INVALID_COORD, 0);
00755 DeleteVehicleNews(this->index, INVALID_STRING_ID);
00756 DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index);
00757 }
00758
00763 void VehicleEnteredDepotThisTick(Vehicle *v)
00764 {
00765
00766 _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
00767
00768
00769
00770
00771
00772
00773 v->vehstatus |= VS_STOPPED;
00774 }
00775
00781 static void RunVehicleDayProc()
00782 {
00783 if (_game_mode != GM_NORMAL) return;
00784
00785
00786 for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
00787 Vehicle *v = Vehicle::Get(i);
00788 if (v == NULL) continue;
00789
00790
00791 if ((v->day_counter & 0x1F) == 0) {
00792 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
00793 if (callback != CALLBACK_FAILED) {
00794 if (HasBit(callback, 0)) TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32);
00795 if (HasBit(callback, 1)) v->colourmap = PAL_NONE;
00796 }
00797 }
00798
00799
00800 v->OnNewDay();
00801 }
00802 }
00803
00804 void CallVehicleTicks()
00805 {
00806 _vehicles_to_autoreplace.Clear();
00807
00808 _age_cargo_skip_counter = (_age_cargo_skip_counter == 0) ? 184 : (_age_cargo_skip_counter - 1);
00809
00810 RunVehicleDayProc();
00811
00812 Station *st;
00813 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
00814
00815 Vehicle *v;
00816 FOR_ALL_VEHICLES(v) {
00817
00818 if (!v->Tick()) {
00819 assert(Vehicle::Get(vehicle_index) == NULL);
00820 continue;
00821 }
00822
00823 assert(Vehicle::Get(vehicle_index) == v);
00824
00825 switch (v->type) {
00826 default: break;
00827
00828 case VEH_TRAIN:
00829 case VEH_ROAD:
00830 case VEH_AIRCRAFT:
00831 case VEH_SHIP:
00832 if (_age_cargo_skip_counter == 0) v->cargo.AgeCargo();
00833
00834 if (v->type == VEH_TRAIN && Train::From(v)->IsWagon()) continue;
00835 if (v->type == VEH_ROAD && !RoadVehicle::From(v)->IsFrontEngine()) continue;
00836
00837 v->travel_time++;
00838
00839 if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
00840
00841 v->motion_counter += v->cur_speed;
00842
00843 if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
00844
00845
00846 if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
00847 }
00848 }
00849
00850 Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
00851 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
00852 v = it->first;
00853
00854 cur_company.Change(v->owner);
00855
00856
00857
00858
00859 if (it->second) v->vehstatus &= ~VS_STOPPED;
00860
00861
00862 int x = v->x_pos;
00863 int y = v->y_pos;
00864 int z = v->z_pos;
00865
00866 const Company *c = Company::Get(_current_company);
00867 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
00868 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
00869 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
00870
00871 if (!IsLocalCompany()) continue;
00872
00873 if (res.Succeeded()) {
00874 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
00875 continue;
00876 }
00877
00878 StringID error_message = res.GetErrorMessage();
00879 if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
00880
00881 if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
00882
00883 StringID message;
00884 if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
00885 message = error_message;
00886 } else {
00887 message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
00888 }
00889
00890 SetDParam(0, v->index);
00891 SetDParam(1, error_message);
00892 AddVehicleNewsItem(message, NS_ADVICE, v->index);
00893 }
00894
00895 cur_company.Restore();
00896 }
00897
00902 static void DoDrawVehicle(const Vehicle *v)
00903 {
00904 SpriteID image = v->cur_image;
00905 PaletteID pal = PAL_NONE;
00906
00907 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
00908
00909 AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
00910 v->x_extent, v->y_extent, v->z_extent, v->z_pos, (v->vehstatus & VS_SHADOW) != 0);
00911 }
00912
00917 void ViewportAddVehicles(DrawPixelInfo *dpi)
00918 {
00919
00920 const int l = dpi->left;
00921 const int r = dpi->left + dpi->width;
00922 const int t = dpi->top;
00923 const int b = dpi->top + dpi->height;
00924
00925
00926 int xl, xu, yl, yu;
00927
00928 if (dpi->width + 70 < (1 << (7 + 6))) {
00929 xl = GB(l - 70, 7, 6);
00930 xu = GB(r, 7, 6);
00931 } else {
00932
00933 xl = 0;
00934 xu = 0x3F;
00935 }
00936
00937 if (dpi->height + 70 < (1 << (6 + 6))) {
00938 yl = GB(t - 70, 6, 6) << 6;
00939 yu = GB(b, 6, 6) << 6;
00940 } else {
00941
00942 yl = 0;
00943 yu = 0x3F << 6;
00944 }
00945
00946 for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
00947 for (int x = xl;; x = (x + 1) & 0x3F) {
00948 const Vehicle *v = _vehicle_position_hash[x + y];
00949
00950 while (v != NULL) {
00951 if (!(v->vehstatus & VS_HIDDEN) &&
00952 l <= v->coord.right &&
00953 t <= v->coord.bottom &&
00954 r >= v->coord.left &&
00955 b >= v->coord.top) {
00956 DoDrawVehicle(v);
00957 }
00958 v = v->next_hash;
00959 }
00960
00961 if (x == xu) break;
00962 }
00963
00964 if (y == yu) break;
00965 }
00966 }
00967
00975 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
00976 {
00977 Vehicle *found = NULL, *v;
00978 uint dist, best_dist = UINT_MAX;
00979
00980 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
00981
00982 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
00983 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
00984
00985 FOR_ALL_VEHICLES(v) {
00986 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
00987 x >= v->coord.left && x <= v->coord.right &&
00988 y >= v->coord.top && y <= v->coord.bottom) {
00989
00990 dist = max(
00991 abs(((v->coord.left + v->coord.right) >> 1) - x),
00992 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
00993 );
00994
00995 if (dist < best_dist) {
00996 found = v;
00997 best_dist = dist;
00998 }
00999 }
01000 }
01001
01002 return found;
01003 }
01004
01009 void DecreaseVehicleValue(Vehicle *v)
01010 {
01011 v->value -= v->value >> 8;
01012 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01013 }
01014
01015 static const byte _breakdown_chance[64] = {
01016 3, 3, 3, 3, 3, 3, 3, 3,
01017 4, 4, 5, 5, 6, 6, 7, 7,
01018 8, 8, 9, 9, 10, 10, 11, 11,
01019 12, 13, 13, 13, 13, 14, 15, 16,
01020 17, 19, 21, 25, 28, 31, 34, 37,
01021 40, 44, 48, 52, 56, 60, 64, 68,
01022 72, 80, 90, 100, 110, 120, 130, 140,
01023 150, 170, 190, 210, 230, 250, 250, 250,
01024 };
01025
01026 void CheckVehicleBreakdown(Vehicle *v)
01027 {
01028 int rel, rel_old;
01029
01030
01031 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
01032 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01033
01034 if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
01035 _settings_game.difficulty.vehicle_breakdowns < 1 ||
01036 v->cur_speed < 5 || _game_mode == GM_MENU) {
01037 return;
01038 }
01039
01040 uint32 r = Random();
01041
01042
01043 int chance = v->breakdown_chance + 1;
01044 if (Chance16I(1, 25, r)) chance += 25;
01045 v->breakdown_chance = min(255, chance);
01046
01047
01048 rel = v->reliability;
01049 if (v->type == VEH_SHIP) rel += 0x6666;
01050
01051
01052 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
01053
01054
01055 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
01056 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
01057 v->breakdown_delay = GB(r, 24, 7) + 0x80;
01058 v->breakdown_chance = 0;
01059 }
01060 }
01061
01068 bool Vehicle::HandleBreakdown()
01069 {
01070
01071
01072
01073
01074
01075 switch (this->breakdown_ctr) {
01076 case 0:
01077 return false;
01078
01079 case 2:
01080 this->breakdown_ctr = 1;
01081
01082 if (this->breakdowns_since_last_service != 255) {
01083 this->breakdowns_since_last_service++;
01084 }
01085
01086 this->MarkDirty();
01087 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01088 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01089
01090 if (this->type == VEH_AIRCRAFT) {
01091
01092 this->vehstatus |= VS_AIRCRAFT_BROKEN;
01093 } else {
01094 this->cur_speed = 0;
01095
01096 if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
01097 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
01098 (this->type == VEH_TRAIN ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
01099 (this->type == VEH_TRAIN ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
01100 }
01101
01102 if (!(this->vehstatus & VS_HIDDEN)) {
01103 EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
01104 if (u != NULL) u->animation_state = this->breakdown_delay * 2;
01105 }
01106 }
01107
01108 case 1:
01109
01110 if (this->type == VEH_AIRCRAFT) return false;
01111
01112
01113 if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
01114 if (--this->breakdown_delay == 0) {
01115 this->breakdown_ctr = 0;
01116 this->MarkDirty();
01117 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01118 }
01119 }
01120 return true;
01121
01122 default:
01123 if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
01124 return false;
01125 }
01126 }
01127
01132 void AgeVehicle(Vehicle *v)
01133 {
01134 if (v->age < MAX_DAY) v->age++;
01135
01136 int age = v->age - v->max_age;
01137 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
01138 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
01139 v->reliability_spd_dec <<= 1;
01140 }
01141
01142 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01143
01144
01145 if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
01146
01147
01148 if (Company::Get(v->owner)->settings.engine_renew && Engine::Get(v->engine_type)->company_avail != 0) return;
01149
01150 StringID str;
01151 if (age == -DAYS_IN_LEAP_YEAR) {
01152 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
01153 } else if (age == 0) {
01154 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
01155 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
01156 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
01157 } else {
01158 return;
01159 }
01160
01161 SetDParam(0, v->index);
01162 AddVehicleNewsItem(str, NS_ADVICE, v->index);
01163 }
01164
01171 uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *colour)
01172 {
01173 int count = 0;
01174 int max = 0;
01175 int cars = 0;
01176 int unloading = 0;
01177 bool loading = false;
01178
01179 const Vehicle *u = v;
01180
01181 const Station *st = Station::GetIfValid(v->last_station_visited);
01182 assert(colour == NULL || st != NULL);
01183
01184
01185 for (; v != NULL; v = v->Next()) {
01186 count += v->cargo.Count();
01187 max += v->cargo_cap;
01188 if (v->cargo_cap != 0 && colour != NULL) {
01189 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
01190 loading |= !(u->current_order.GetLoadType() & OLFB_NO_LOAD) && st->goods[v->cargo_type].days_since_pickup != 255;
01191 cars++;
01192 }
01193 }
01194
01195 if (colour != NULL) {
01196 if (unloading == 0 && loading) {
01197 *colour = STR_PERCENT_UP;
01198 } else if (cars == unloading || !loading) {
01199 *colour = STR_PERCENT_DOWN;
01200 } else {
01201 *colour = STR_PERCENT_UP_DOWN;
01202 }
01203 }
01204
01205
01206 if (max == 0) return 100;
01207
01208
01209 return (count * 100) / max;
01210 }
01211
01216 void VehicleEnterDepot(Vehicle *v)
01217 {
01218
01219 assert(v == v->First());
01220
01221 switch (v->type) {
01222 case VEH_TRAIN: {
01223 Train *t = Train::From(v);
01224 SetWindowClassesDirty(WC_TRAINS_LIST);
01225
01226 SetDepotReservation(t->tile, false);
01227 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
01228
01229 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
01230 t->wait_counter = 0;
01231 t->force_proceed = TFP_NONE;
01232 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
01233 t->ConsistChanged(true);
01234 break;
01235 }
01236
01237 case VEH_ROAD:
01238 SetWindowClassesDirty(WC_ROADVEH_LIST);
01239 break;
01240
01241 case VEH_SHIP: {
01242 SetWindowClassesDirty(WC_SHIPS_LIST);
01243 Ship *ship = Ship::From(v);
01244 ship->state = TRACK_BIT_DEPOT;
01245 ship->UpdateCache();
01246 ship->UpdateViewport(true, true);
01247 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01248 break;
01249 }
01250
01251 case VEH_AIRCRAFT:
01252 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01253 HandleAircraftEnterHangar(Aircraft::From(v));
01254 break;
01255 default: NOT_REACHED();
01256 }
01257 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01258
01259 if (v->type != VEH_TRAIN) {
01260
01261
01262 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01263 }
01264 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01265
01266 v->vehstatus |= VS_HIDDEN;
01267 v->cur_speed = 0;
01268
01269 VehicleServiceInDepot(v);
01270
01271 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01272
01273 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01274 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01275
01276 const Order *real_order = v->GetOrder(v->cur_real_order_index);
01277 Order t = v->current_order;
01278 v->current_order.MakeDummy();
01279
01280
01281
01282 if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
01283 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
01284 (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
01285
01286 return;
01287 }
01288
01289 if (t.IsRefit()) {
01290 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01291 CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
01292 cur_company.Restore();
01293
01294 if (cost.Failed()) {
01295 _vehicles_to_autoreplace[v] = false;
01296 if (v->owner == _local_company) {
01297
01298 SetDParam(0, v->index);
01299 AddVehicleNewsItem(STR_NEWS_ORDER_REFIT_FAILED, NS_ADVICE, v->index);
01300 }
01301 } else if (cost.GetCost() != 0) {
01302 v->profit_this_year -= cost.GetCost() << 8;
01303 if (v->owner == _local_company) {
01304 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01305 }
01306 }
01307 }
01308
01309 if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01310
01311 v->DeleteUnreachedAutoOrders();
01312 UpdateVehicleTimetable(v, true);
01313 v->IncrementAutoOrderIndex();
01314 }
01315 if (t.GetDepotActionType() & ODATFB_HALT) {
01316
01317 _vehicles_to_autoreplace[v] = false;
01318 if (v->owner == _local_company) {
01319 SetDParam(0, v->index);
01320 AddVehicleNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, NS_ADVICE, v->index);
01321 }
01322 AI::NewEvent(v->owner, new AIEventVehicleWaitingInDepot(v->index));
01323 }
01324 }
01325 }
01326
01327
01335 void VehicleMove(Vehicle *v, bool update_viewport)
01336 {
01337 int img = v->cur_image;
01338 Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
01339 const Sprite *spr = GetSprite(img, ST_NORMAL);
01340
01341 pt.x += spr->x_offs;
01342 pt.y += spr->y_offs;
01343
01344 UpdateVehiclePosHash(v, pt.x, pt.y);
01345
01346 Rect old_coord = v->coord;
01347 v->coord.left = pt.x;
01348 v->coord.top = pt.y;
01349 v->coord.right = pt.x + spr->width + 2;
01350 v->coord.bottom = pt.y + spr->height + 2;
01351
01352 if (update_viewport) {
01353 MarkAllViewportsDirty(
01354 min(old_coord.left, v->coord.left),
01355 min(old_coord.top, v->coord.top),
01356 max(old_coord.right, v->coord.right) + 1,
01357 max(old_coord.bottom, v->coord.bottom) + 1
01358 );
01359 }
01360 }
01361
01370 void MarkSingleVehicleDirty(const Vehicle *v)
01371 {
01372 MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1, v->coord.bottom + 1);
01373 }
01374
01380 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01381 {
01382 static const int8 _delta_coord[16] = {
01383 -1,-1,-1, 0, 1, 1, 1, 0,
01384 -1, 0, 1, 1, 1, 0,-1,-1,
01385 };
01386
01387 int x = v->x_pos + _delta_coord[v->direction];
01388 int y = v->y_pos + _delta_coord[v->direction + 8];
01389
01390 GetNewVehiclePosResult gp;
01391 gp.x = x;
01392 gp.y = y;
01393 gp.old_tile = v->tile;
01394 gp.new_tile = TileVirtXY(x, y);
01395 return gp;
01396 }
01397
01398 static const Direction _new_direction_table[] = {
01399 DIR_N, DIR_NW, DIR_W,
01400 DIR_NE, DIR_SE, DIR_SW,
01401 DIR_E, DIR_SE, DIR_S
01402 };
01403
01404 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01405 {
01406 int i = 0;
01407
01408 if (y >= v->y_pos) {
01409 if (y != v->y_pos) i += 3;
01410 i += 3;
01411 }
01412
01413 if (x >= v->x_pos) {
01414 if (x != v->x_pos) i++;
01415 i++;
01416 }
01417
01418 Direction dir = v->direction;
01419
01420 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01421 if (dirdiff == DIRDIFF_SAME) return dir;
01422 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01423 }
01424
01434 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01435 {
01436 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01437 }
01438
01446 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01447 {
01448
01449 const Vehicle *v;
01450 FOR_ALL_VEHICLES(v) {
01451 if (v->type == type && v->owner == owner) {
01452 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01453 }
01454 }
01455
01456 if (this->maxid == 0) return;
01457
01458
01459
01460
01461 this->cache = CallocT<bool>(this->maxid + 2);
01462
01463
01464 FOR_ALL_VEHICLES(v) {
01465 if (v->type == type && v->owner == owner) {
01466 this->cache[v->unitnumber] = true;
01467 }
01468 }
01469 }
01470
01472 UnitID FreeUnitIDGenerator::NextID()
01473 {
01474 if (this->maxid <= this->curid) return ++this->curid;
01475
01476 while (this->cache[++this->curid]) { }
01477
01478 return this->curid;
01479 }
01480
01486 UnitID GetFreeUnitNumber(VehicleType type)
01487 {
01488
01489 uint max_veh;
01490 switch (type) {
01491 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
01492 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
01493 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
01494 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
01495 default: NOT_REACHED();
01496 }
01497
01498 uint amounts[4];
01499 CountCompanyVehicles(_current_company, amounts);
01500 assert((uint)type < lengthof(amounts));
01501 if (amounts[type] >= max_veh) return UINT16_MAX;
01502
01503 FreeUnitIDGenerator gen(type, _current_company);
01504
01505 return gen.NextID();
01506 }
01507
01508
01517 bool CanBuildVehicleInfrastructure(VehicleType type)
01518 {
01519 assert(IsCompanyBuildableVehicleType(type));
01520
01521 if (!Company::IsValidID(_local_company)) return false;
01522 if (!_settings_client.gui.disable_unsuitable_building) return true;
01523
01524 UnitID max;
01525 switch (type) {
01526 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
01527 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
01528 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
01529 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01530 default: NOT_REACHED();
01531 }
01532
01533
01534 if (max > 0) {
01535
01536 const Engine *e;
01537 FOR_ALL_ENGINES_OF_TYPE(e, type) {
01538 if (HasBit(e->company_avail, _local_company)) return true;
01539 }
01540 return false;
01541 }
01542
01543
01544 const Vehicle *v;
01545 FOR_ALL_VEHICLES(v) {
01546 if (v->owner == _local_company && v->type == type) return true;
01547 }
01548
01549 return false;
01550 }
01551
01552
01560 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
01561 {
01562 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01563 const Engine *e = Engine::Get(engine_type);
01564 switch (e->type) {
01565 default: NOT_REACHED();
01566 case VEH_TRAIN:
01567 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
01568
01569
01570 engine_type = parent_engine_type;
01571 e = Engine::Get(engine_type);
01572
01573 }
01574
01575 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01576 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01577 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
01578 if (!CargoSpec::Get(cargo_type)->is_freight) {
01579 if (parent_engine_type == INVALID_ENGINE) {
01580 return LS_PASSENGER_WAGON_STEAM;
01581 } else {
01582 switch (RailVehInfo(parent_engine_type)->engclass) {
01583 default: NOT_REACHED();
01584 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
01585 case EC_DIESEL: return LS_PASSENGER_WAGON_DIESEL;
01586 case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
01587 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
01588 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
01589 }
01590 }
01591 } else {
01592 return LS_FREIGHT_WAGON;
01593 }
01594 } else {
01595 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
01596
01597 switch (e->u.rail.engclass) {
01598 default: NOT_REACHED();
01599 case EC_STEAM: return LS_STEAM;
01600 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
01601 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
01602 case EC_MONORAIL: return LS_MONORAIL;
01603 case EC_MAGLEV: return LS_MAGLEV;
01604 }
01605 }
01606
01607 case VEH_ROAD:
01608
01609 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01610 engine_type = parent_engine_type;
01611 e = Engine::Get(engine_type);
01612 cargo_type = v->First()->cargo_type;
01613 }
01614 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01615 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01616
01617
01618 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
01619
01620 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01621 } else {
01622
01623 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01624 }
01625
01626 case VEH_SHIP:
01627 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01628 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01629 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01630
01631 case VEH_AIRCRAFT:
01632 switch (e->u.air.subtype) {
01633 case AIR_HELI: return LS_HELICOPTER;
01634 case AIR_CTOL: return LS_SMALL_PLANE;
01635 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
01636 default: NOT_REACHED();
01637 }
01638 }
01639 }
01640
01650 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
01651 {
01652 const Company *c = Company::Get(company);
01653 LiveryScheme scheme = LS_DEFAULT;
01654
01655
01656
01657 if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
01658
01659 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
01660
01661
01662 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01663 }
01664
01665 return &c->livery[scheme];
01666 }
01667
01668
01669 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01670 {
01671 PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01672
01673
01674 if (map != PAL_NONE) return map;
01675
01676 const Engine *e = Engine::Get(engine_type);
01677
01678
01679 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
01680 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01681
01682 if (callback != CALLBACK_FAILED) {
01683 assert_compile(PAL_NONE == 0);
01684 map = GB(callback, 0, 14);
01685
01686
01687 if (!HasBit(callback, 14)) {
01688
01689 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01690 return map;
01691 }
01692 }
01693 }
01694
01695 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
01696
01697 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
01698
01699
01700 if (!Company::IsValidID(company)) return map;
01701
01702 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
01703
01704 map += livery->colour1;
01705 if (twocc) map += livery->colour2 * 16;
01706
01707
01708 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01709 return map;
01710 }
01711
01718 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
01719 {
01720 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01721 }
01722
01728 PaletteID GetVehiclePalette(const Vehicle *v)
01729 {
01730 if (v->IsGroundVehicle()) {
01731 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
01732 }
01733
01734 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01735 }
01736
01745 uint GetVehicleCapacity(const Vehicle *v, uint16 *mail_capacity)
01746 {
01747 if (mail_capacity != NULL) *mail_capacity = 0;
01748 const Engine *e = Engine::Get(v->engine_type);
01749
01750 if (!e->CanCarryCargo()) return 0;
01751
01752 if (mail_capacity != NULL && e->type == VEH_AIRCRAFT && IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
01753 *mail_capacity = GetVehicleProperty(v, PROP_AIRCRAFT_MAIL_CAPACITY, e->u.air.mail_capacity);
01754 }
01755 CargoID default_cargo = e->GetDefaultCargoType();
01756
01757
01758
01759 if (HasBit(e->info.callback_mask, CBM_VEHICLE_REFIT_CAPACITY) &&
01760 (default_cargo != v->cargo_type || v->cargo_subtype != 0)) {
01761 uint16 callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
01762 if (callback != CALLBACK_FAILED) return callback;
01763 }
01764
01765
01766 uint capacity;
01767 switch (e->type) {
01768 case VEH_TRAIN: capacity = GetVehicleProperty(v, PROP_TRAIN_CARGO_CAPACITY, e->u.rail.capacity); break;
01769 case VEH_ROAD: capacity = GetVehicleProperty(v, PROP_ROADVEH_CARGO_CAPACITY, e->u.road.capacity); break;
01770 case VEH_SHIP: capacity = GetVehicleProperty(v, PROP_SHIP_CARGO_CAPACITY, e->u.ship.capacity); break;
01771 case VEH_AIRCRAFT: capacity = GetVehicleProperty(v, PROP_AIRCRAFT_PASSENGER_CAPACITY, e->u.air.passenger_capacity); break;
01772 default: NOT_REACHED();
01773 }
01774
01775
01776
01777 if (e->type != VEH_SHIP) {
01778 if (e->type == VEH_AIRCRAFT) {
01779 if (!IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
01780 capacity += GetVehicleProperty(v, PROP_AIRCRAFT_MAIL_CAPACITY, e->u.air.mail_capacity);
01781 }
01782 if (v->cargo_type == CT_MAIL) return capacity;
01783 } else {
01784 switch (default_cargo) {
01785 case CT_PASSENGERS: break;
01786 case CT_MAIL:
01787 case CT_GOODS: capacity *= 2; break;
01788 default: capacity *= 4; break;
01789 }
01790 }
01791 switch (v->cargo_type) {
01792 case CT_PASSENGERS: break;
01793 case CT_MAIL:
01794 case CT_GOODS: capacity /= 2; break;
01795 default: capacity /= 4; break;
01796 }
01797 }
01798
01799 return capacity;
01800 }
01801
01805 void Vehicle::DeleteUnreachedAutoOrders()
01806 {
01807 if (this->IsGroundVehicle()) {
01808 uint16 &gv_flags = this->GetGroundVehicleFlags();
01809 if (HasBit(gv_flags, GVF_SUPPRESS_AUTOMATIC_ORDERS)) {
01810
01811 ClrBit(gv_flags, GVF_SUPPRESS_AUTOMATIC_ORDERS);
01812 this->cur_auto_order_index = this->cur_real_order_index;
01813 InvalidateVehicleOrder(this, 0);
01814 return;
01815 }
01816 }
01817
01818 const Order *order = this->GetOrder(this->cur_auto_order_index);
01819 while (order != NULL) {
01820 if (this->cur_auto_order_index == this->cur_real_order_index) break;
01821
01822 if (order->IsType(OT_AUTOMATIC)) {
01823
01824 order = order->next;
01825 DeleteOrder(this, this->cur_auto_order_index);
01826 } else {
01827
01828 order = order->next;
01829 this->cur_auto_order_index++;
01830 }
01831
01832
01833 if (order == NULL) {
01834 order = this->GetOrder(0);
01835 this->cur_auto_order_index = 0;
01836 }
01837 }
01838 }
01839
01845 void Vehicle::BeginLoading(StationID station)
01846 {
01847 assert(IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP);
01848
01849 this->last_station_visited = station;
01850
01851 if (this->current_order.IsType(OT_GOTO_STATION) &&
01852 this->current_order.GetDestination() == this->last_station_visited) {
01853 this->DeleteUnreachedAutoOrders();
01854
01855
01856 this->current_order.MakeLoading(true);
01857 UpdateVehicleTimetable(this, true);
01858
01859
01860
01861
01862
01863
01864 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
01865
01866 } else {
01867 assert(this->IsGroundVehicle());
01868 bool suppress_automatic_orders = HasBit(this->GetGroundVehicleFlags(), GVF_SUPPRESS_AUTOMATIC_ORDERS);
01869
01870
01871
01872
01873 Order *in_list = this->GetOrder(this->cur_auto_order_index);
01874 if (in_list != NULL &&
01875 (!in_list->IsType(OT_AUTOMATIC) ||
01876 in_list->GetDestination() != this->last_station_visited)) {
01877
01878 Order *prev_order = this->cur_auto_order_index > 0 ? this->GetOrder(this->cur_auto_order_index - 1) : NULL;
01879 if (prev_order == NULL ||
01880 (!prev_order->IsType(OT_AUTOMATIC) && !prev_order->IsType(OT_GOTO_STATION)) ||
01881 prev_order->GetDestination() != this->last_station_visited) {
01882
01883
01884
01885 int target_index = this->cur_auto_order_index;
01886 bool found = false;
01887 while (target_index != this->cur_real_order_index) {
01888 const Order *order = this->GetOrder(target_index);
01889 if (order->IsType(OT_AUTOMATIC) && order->GetDestination() == this->last_station_visited) {
01890 found = true;
01891 break;
01892 }
01893 target_index++;
01894 if (target_index >= this->orders.list->GetNumOrders()) target_index = 0;
01895 assert(target_index != this->cur_auto_order_index);
01896 }
01897
01898 if (found) {
01899 if (suppress_automatic_orders) {
01900
01901 this->cur_auto_order_index = target_index;
01902 InvalidateVehicleOrder(this, 0);
01903 } else {
01904
01905 const Order *order = this->GetOrder(this->cur_auto_order_index);
01906 while (!order->IsType(OT_AUTOMATIC) || order->GetDestination() != this->last_station_visited) {
01907 if (order->IsType(OT_AUTOMATIC)) {
01908
01909 order = order->next;
01910 DeleteOrder(this, this->cur_auto_order_index);
01911 } else {
01912
01913 order = order->next;
01914 this->cur_auto_order_index++;
01915 }
01916
01917
01918 if (order == NULL) {
01919 order = this->GetOrder(0);
01920 this->cur_auto_order_index = 0;
01921 }
01922 assert(order != NULL);
01923 }
01924 }
01925 } else if (!suppress_automatic_orders && this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID && Order::CanAllocateItem()) {
01926
01927 Order *auto_order = new Order();
01928 auto_order->MakeAutomatic(this->last_station_visited);
01929 InsertOrder(this, auto_order, this->cur_auto_order_index);
01930 if (this->cur_auto_order_index > 0) --this->cur_auto_order_index;
01931 this->current_order.index = auto_order->index;
01932
01933
01934
01935 uint16 &gv_flags = this->GetGroundVehicleFlags();
01936 ClrBit(gv_flags, GVF_SUPPRESS_AUTOMATIC_ORDERS);
01937 }
01938 }
01939 }
01940 this->current_order.MakeLoading(false);
01941 }
01942
01943 UpdateVehicleRouteLinks(this, station);
01944
01945
01946
01947 this->last_order_id = this->current_order.index;
01948 this->last_station_loaded = station;
01949
01950 Station *last_visited = Station::Get(this->last_station_visited);
01951 last_visited->loading_vehicles.push_back(this);
01952
01953
01954 CargoID cid;
01955 FOR_EACH_SET_CARGO_ID(cid, this->vcache.cached_cargo_mask) {
01956
01957 if (CargoHasDestinations(cid) && last_visited->goods[cid].cargo_counter == 0) {
01958 last_visited->goods[cid].cargo.UpdateCargoNextHop(last_visited, cid);
01959 last_visited->goods[cid].cargo_counter = _settings_game.economy.cargodest.route_recalc_delay;
01960 }
01961 }
01962
01963 PrepareUnload(this);
01964
01965 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
01966 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01967 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01968 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
01969
01970 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
01971 this->cur_speed = 0;
01972 this->MarkDirty();
01973 }
01974
01979 void Vehicle::LeaveStation()
01980 {
01981 assert(this->current_order.IsType(OT_LOADING));
01982
01983 delete this->cargo_payment;
01984
01985
01986 if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
01987
01988
01989 this->travel_time = 0;
01990
01991 this->current_order.MakeLeaveStation();
01992 Station *st = Station::Get(this->last_station_visited);
01993 st->loading_vehicles.remove(this);
01994
01995 HideFillingPercent(&this->fill_percent_te_id);
01996
01997 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
01998
01999 if (IsTileType(this->tile, MP_STATION)) TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
02000
02001 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
02002 }
02003 }
02004
02005
02011 void Vehicle::HandleLoading(bool mode)
02012 {
02013 switch (this->current_order.GetType()) {
02014 case OT_LOADING: {
02015 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
02016
02017
02018 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
02019
02020 this->PlayLeaveStationSound();
02021
02022 this->LeaveStation();
02023
02024
02025 const Order *order = this->GetOrder(this->cur_auto_order_index);
02026 if (order == NULL ||
02027 (!order->IsType(OT_AUTOMATIC) && !order->IsType(OT_GOTO_STATION)) ||
02028 order->GetDestination() != this->last_station_visited) {
02029 return;
02030 }
02031 break;
02032 }
02033
02034 case OT_DUMMY: break;
02035
02036 default: return;
02037 }
02038
02039 this->IncrementAutoOrderIndex();
02040 }
02041
02048 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
02049 {
02050 CommandCost ret = CheckOwnership(this->owner);
02051 if (ret.Failed()) return ret;
02052
02053 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
02054 if (this->IsStoppedInDepot()) return CMD_ERROR;
02055
02056 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
02057 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
02058 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
02059
02060
02061
02062 if (flags & DC_EXEC) {
02063 this->current_order.SetDepotOrderType(ODTF_MANUAL);
02064 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
02065 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
02066 }
02067 return CommandCost();
02068 }
02069
02070 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR;
02071 if (flags & DC_EXEC) {
02072
02073
02074 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
02075
02076 if (this->IsGroundVehicle()) {
02077 uint16 &gv_flags = this->GetGroundVehicleFlags();
02078 SetBit(gv_flags, GVF_SUPPRESS_AUTOMATIC_ORDERS);
02079 }
02080
02081 this->current_order.MakeDummy();
02082 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
02083 }
02084 return CommandCost();
02085 }
02086
02087 TileIndex location;
02088 DestinationID destination;
02089 bool reverse;
02090 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};
02091 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
02092
02093 if (flags & DC_EXEC) {
02094 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
02095
02096 if (this->IsGroundVehicle()) {
02097 uint16 &gv_flags = this->GetGroundVehicleFlags();
02098 SetBit(gv_flags, GVF_SUPPRESS_AUTOMATIC_ORDERS);
02099 }
02100
02101 this->dest_tile = location;
02102 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
02103 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
02104 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
02105
02106
02107 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
02108
02109 if (this->type == VEH_AIRCRAFT) {
02110 Aircraft *a = Aircraft::From(this);
02111 if (a->state == FLYING && a->targetairport != destination) {
02112
02113 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
02114 AircraftNextAirportPos_and_Order(a);
02115 }
02116 }
02117 }
02118
02119 return CommandCost();
02120
02121 }
02122
02127 void Vehicle::UpdateVisualEffect(bool allow_power_change)
02128 {
02129 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02130 const Engine *e = Engine::Get(this->engine_type);
02131
02132
02133 byte visual_effect;
02134 switch (e->type) {
02135 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
02136 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
02137 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
02138 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
02139 }
02140
02141
02142 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
02143 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
02144
02145 if (callback != CALLBACK_FAILED) {
02146 callback = GB(callback, 0, 8);
02147
02148
02149 if (callback == VE_DEFAULT) {
02150 assert(HasBit(callback, VE_DISABLE_EFFECT));
02151 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
02152 }
02153 visual_effect = callback;
02154 }
02155 }
02156
02157
02158 if (visual_effect == VE_DEFAULT ||
02159 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
02160
02161
02162 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
02163 if (visual_effect == VE_DEFAULT) {
02164 visual_effect = 1 << VE_DISABLE_EFFECT;
02165 } else {
02166 SetBit(visual_effect, VE_DISABLE_EFFECT);
02167 }
02168 } else {
02169 if (visual_effect == VE_DEFAULT) {
02170
02171 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
02172 }
02173 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
02174 }
02175 }
02176
02177 this->vcache.cached_vis_effect = visual_effect;
02178
02179 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
02180 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02181 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
02182 }
02183 }
02184
02185 static const int8 _vehicle_smoke_pos[8] = {
02186 1, 1, 1, 0, -1, -1, -1, 0
02187 };
02188
02193 void Vehicle::ShowVisualEffect() const
02194 {
02195 assert(this->IsPrimaryVehicle());
02196 bool sound = false;
02197
02198
02199
02200
02201
02202
02203 if (_settings_game.vehicle.smoke_amount == 0 ||
02204 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
02205 this->cur_speed < 2) {
02206 return;
02207 }
02208 if (this->type == VEH_TRAIN) {
02209 const Train *t = Train::From(this);
02210
02211
02212
02213
02214 if (HasBit(t->flags, VRF_REVERSING) ||
02215 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
02216 t->cur_speed >= t->Train::GetCurrentMaxSpeed())) {
02217 return;
02218 }
02219 }
02220
02221 const Vehicle *v = this;
02222
02223 do {
02224 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
02225 byte effect_type = GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
02226 bool disable_effect = HasBit(v->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
02227
02228
02229
02230
02231
02232
02233
02234
02235 if (disable_effect ||
02236 v->vehstatus & VS_HIDDEN ||
02237 (MayHaveBridgeAbove(v->tile) && IsBridgeAbove(v->tile)) ||
02238 IsDepotTile(v->tile) ||
02239 IsTunnelTile(v->tile) ||
02240 (v->type == VEH_TRAIN &&
02241 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
02242 continue;
02243 }
02244
02245 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
02246 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
02247
02248 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
02249 x = -x;
02250 y = -y;
02251 }
02252
02253 switch (effect_type) {
02254 case VE_TYPE_STEAM:
02255
02256
02257
02258
02259
02260 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / this->vcache.cached_max_speed))) == 0) {
02261 CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
02262 sound = true;
02263 }
02264 break;
02265
02266 case VE_TYPE_DIESEL: {
02267
02268
02269
02270
02271
02272
02273
02274
02275
02276
02277
02278 int power_weight_effect = 0;
02279 if (v->type == VEH_TRAIN) {
02280 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
02281 }
02282 if (this->cur_speed < (this->vcache.cached_max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
02283 Chance16((64 - ((this->cur_speed << 5) / this->vcache.cached_max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
02284 CreateEffectVehicleRel(v, x, y, 10, EV_DIESEL_SMOKE);
02285 sound = true;
02286 }
02287 break;
02288 }
02289
02290 case VE_TYPE_ELECTRIC:
02291
02292
02293
02294
02295
02296
02297 if (GB(v->tick_counter, 0, 2) == 0 &&
02298 Chance16((6 - ((this->cur_speed << 2) / this->vcache.cached_max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
02299 CreateEffectVehicleRel(v, x, y, 10, EV_ELECTRIC_SPARK);
02300 sound = true;
02301 }
02302 break;
02303
02304 default:
02305 break;
02306 }
02307 } while ((v = v->Next()) != NULL);
02308
02309 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
02310 }
02311
02316 void Vehicle::SetNext(Vehicle *next)
02317 {
02318 assert(this != next);
02319
02320 if (this->next != NULL) {
02321
02322 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02323 v->first = this->next;
02324 }
02325 this->next->previous = NULL;
02326 }
02327
02328 this->next = next;
02329
02330 if (this->next != NULL) {
02331
02332 if (this->next->previous != NULL) this->next->previous->next = NULL;
02333 this->next->previous = this;
02334 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02335 v->first = this->first;
02336 }
02337 }
02338 }
02339
02345 void Vehicle::AddToShared(Vehicle *shared_chain)
02346 {
02347 assert(this->previous_shared == NULL && this->next_shared == NULL);
02348
02349 if (shared_chain->orders.list == NULL) {
02350 assert(shared_chain->previous_shared == NULL);
02351 assert(shared_chain->next_shared == NULL);
02352 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
02353 }
02354
02355 this->next_shared = shared_chain->next_shared;
02356 this->previous_shared = shared_chain;
02357
02358 shared_chain->next_shared = this;
02359
02360 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
02361
02362 shared_chain->orders.list->AddVehicle(this);
02363 }
02364
02368 void Vehicle::RemoveFromShared()
02369 {
02370
02371
02372 bool were_first = (this->FirstShared() == this);
02373 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
02374
02375 this->orders.list->RemoveVehicle(this);
02376
02377 if (!were_first) {
02378
02379 this->previous_shared->next_shared = this->NextShared();
02380 }
02381
02382 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
02383
02384
02385 if (this->orders.list->GetNumVehicles() == 1) {
02386
02387 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
02388 InvalidateVehicleOrder(this->FirstShared(), 0);
02389 } else if (were_first) {
02390
02391
02392 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
02393 }
02394
02395 this->next_shared = NULL;
02396 this->previous_shared = NULL;
02397 }
02398
02399 void VehiclesYearlyLoop()
02400 {
02401 Vehicle *v;
02402 FOR_ALL_VEHICLES(v) {
02403 if (v->IsPrimaryVehicle()) {
02404
02405 Money profit = v->GetDisplayProfitThisYear();
02406 if (v->age >= 730 && profit < 0) {
02407 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
02408 SetDParam(0, v->index);
02409 SetDParam(1, profit);
02410 AddVehicleNewsItem(
02411 STR_NEWS_VEHICLE_IS_UNPROFITABLE,
02412 NS_ADVICE,
02413 v->index
02414 );
02415 }
02416 AI::NewEvent(v->owner, new AIEventVehicleUnprofitable(v->index));
02417 }
02418
02419 v->profit_last_year = v->profit_this_year;
02420 v->profit_this_year = 0;
02421 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
02422 }
02423 }
02424 }
02425
02426
02436 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
02437 {
02438 const Engine *e = Engine::GetIfValid(engine_type);
02439 assert(e != NULL);
02440
02441 switch (e->type) {
02442 case VEH_TRAIN:
02443 return (st->facilities & FACIL_TRAIN) != 0;
02444
02445 case VEH_ROAD:
02446
02447
02448
02449 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
02450
02451 case VEH_SHIP:
02452 return (st->facilities & FACIL_DOCK) != 0;
02453
02454 case VEH_AIRCRAFT:
02455 return (st->facilities & FACIL_AIRPORT) != 0 &&
02456 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
02457
02458 default:
02459 return false;
02460 }
02461 }
02462
02469 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
02470 {
02471 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
02472
02473 return CanVehicleUseStation(v->engine_type, st);
02474 }
02475
02481 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
02482 {
02483 assert(this->IsGroundVehicle());
02484 if (this->type == VEH_TRAIN) {
02485 return &Train::From(this)->gcache;
02486 } else {
02487 return &RoadVehicle::From(this)->gcache;
02488 }
02489 }
02490
02496 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
02497 {
02498 assert(this->IsGroundVehicle());
02499 if (this->type == VEH_TRAIN) {
02500 return &Train::From(this)->gcache;
02501 } else {
02502 return &RoadVehicle::From(this)->gcache;
02503 }
02504 }
02505
02511 uint16 &Vehicle::GetGroundVehicleFlags()
02512 {
02513 assert(this->IsGroundVehicle());
02514 if (this->type == VEH_TRAIN) {
02515 return Train::From(this)->gv_flags;
02516 } else {
02517 return RoadVehicle::From(this)->gv_flags;
02518 }
02519 }
02520
02526 const uint16 &Vehicle::GetGroundVehicleFlags() const
02527 {
02528 assert(this->IsGroundVehicle());
02529 if (this->type == VEH_TRAIN) {
02530 return Train::From(this)->gv_flags;
02531 } else {
02532 return RoadVehicle::From(this)->gv_flags;
02533 }
02534 }
02535
02544 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
02545 {
02546 if (v->type == VEH_TRAIN) {
02547 Train *u = Train::From(v);
02548
02549 u = u->GetFirstEnginePart();
02550
02551
02552 for (; u != NULL && num_vehicles > 0; num_vehicles--) {
02553 do {
02554
02555 set.Include(u->index);
02556
02557
02558 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
02559
02560 u = u->Next();
02561 } while (u != NULL && u->IsArticulatedPart());
02562 }
02563 }
02564 }