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