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 "infrastructure_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 const Engine *e = Engine::Get(v->engine_type);
00094 if (v->type == VEH_TRAIN) {
00095 if (v->Next() != NULL) VehicleServiceInDepot(v->Next());
00096 if (!(Train::From(v)->IsEngine()) && !(Train::From(v)->IsRearDualheaded())) return;
00097 ClrBit(Train::From(v)->flags,VRF_NEED_REPAIR);
00098 const RailVehicleInfo *rvi = &e->u.rail;
00099 v->vcache.cached_max_speed = rvi->max_speed;
00100 if (Train::From(v)->IsFrontEngine()) {
00101 Train::From(v)->ConsistChanged(true);
00102 CLRBITS(Train::From(v)->flags, (1 << VRF_BREAKDOWN_BRAKING) | VRF_IS_BROKEN );
00103 }
00104 }
00105 v->date_of_last_service = _date;
00106 v->breakdowns_since_last_service = 0;
00107 v->reliability = e->reliability;
00108 v->breakdown_ctr = 0;
00109 v->vehstatus &= ~VS_AIRCRAFT_BROKEN;
00110 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
00111 }
00112
00119 bool Vehicle::NeedsServicing() const
00120 {
00121
00122
00123 if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
00124
00125
00126 const Company *c = Company::Get(this->owner);
00127 if ((c->settings.vehicle.servint_ispercent ?
00128 (this->reliability >= Engine::Get(this->engine_type)->reliability * (100 - this->service_interval) / 100) :
00129 (this->date_of_last_service + this->service_interval >= _date))
00130 && !(this->type == VEH_TRAIN && HasBit(Train::From(this)->flags ,VRF_NEED_REPAIR))) {
00131 return false;
00132 }
00133
00134
00135
00136 if (!_settings_game.order.no_servicing_if_no_breakdowns ||
00137 _settings_game.difficulty.vehicle_breakdowns != 0) {
00138 return true;
00139 }
00140
00141
00142
00143
00144 bool pending_replace = false;
00145 Money needed_money = c->settings.engine_renew_money;
00146 if (needed_money > c->money) return false;
00147
00148 for (const Vehicle *v = this; v != NULL; v = (v->type == VEH_TRAIN) ? Train::From(v)->GetNextUnit() : NULL) {
00149 EngineID new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id);
00150
00151
00152 if (new_engine == INVALID_ENGINE || !HasBit(Engine::Get(new_engine)->company_avail, v->owner)) continue;
00153
00154
00155 uint32 available_cargo_types, union_mask;
00156 GetArticulatedRefitMasks(new_engine, true, &union_mask, &available_cargo_types);
00157
00158 if (union_mask != 0) {
00159 CargoID cargo_type;
00160
00161 if (IsArticulatedVehicleCarryingDifferentCargos(v, &cargo_type)) continue;
00162
00163
00164 if (cargo_type != CT_INVALID) {
00165
00166 if (!HasBit(available_cargo_types, cargo_type)) continue;
00167 }
00168 }
00169
00170
00171
00172 pending_replace = true;
00173 needed_money += 2 * Engine::Get(new_engine)->GetCost();
00174 if (needed_money > c->money) return false;
00175 }
00176
00177 return pending_replace;
00178 }
00179
00185 bool Vehicle::NeedsAutomaticServicing() const
00186 {
00187 if (_settings_game.order.gotodepot && this->HasDepotOrder()) return false;
00188 if (this->current_order.IsType(OT_LOADING)) return false;
00189 if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
00190 return NeedsServicing();
00191 }
00192
00193 uint Vehicle::Crash(bool flooded)
00194 {
00195 assert((this->vehstatus & VS_CRASHED) == 0);
00196 assert(this->Previous() == NULL);
00197
00198 uint pass = 0;
00199
00200 if (this->IsPrimaryVehicle()) this->vehstatus |= VS_STOPPED;
00201
00202 for (Vehicle *v = this; v != NULL; v = v->Next()) {
00203 if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo.Count();
00204 v->vehstatus |= VS_CRASHED;
00205 MarkSingleVehicleDirty(v);
00206 }
00207
00208 if (_settings_game.order.timetable_separation) this->ClearSeparation();
00209 if (_settings_game.order.timetable_separation) ClrBit(this->vehicle_flags, VF_TIMETABLE_STARTED);
00210
00211
00212 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00213 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
00214 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
00215 SetWindowDirty(WC_VEHICLE_DEPOT, this->tile);
00216
00217 return pass;
00218 }
00219
00220 bool Vehicle::IsDrawn() const
00221 {
00222 return !(this->vehstatus & VS_HIDDEN) ||
00223 (!IsTransparencySet(TO_TUNNELS) && !(_game_mode == GM_MENU) &&
00224 ((this->type == VEH_TRAIN && Train::From(this)->track == TRACK_BIT_WORMHOLE) ||
00225 (this->type == VEH_ROAD && RoadVehicle::From(this)->state == RVSB_WORMHOLE)));
00226 }
00227
00236 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
00237 {
00238 const Engine *e = Engine::Get(engine);
00239 uint32 grfid = e->grf_prop.grffile->grfid;
00240 GRFConfig *grfconfig = GetGRFConfig(grfid);
00241
00242 if (!HasBit(grfconfig->grf_bugs, bug_type)) {
00243 SetBit(grfconfig->grf_bugs, bug_type);
00244 SetDParamStr(0, grfconfig->GetName());
00245 SetDParam(1, engine);
00246 ShowErrorMessage(part1, part2, WL_CRITICAL);
00247 if (!_networking) DoCommand(0, critical ? PM_PAUSED_ERROR : PM_PAUSED_NORMAL, 1, DC_EXEC, CMD_PAUSE);
00248 }
00249
00250
00251 char buffer[512];
00252
00253 SetDParamStr(0, grfconfig->GetName());
00254 GetString(buffer, part1, lastof(buffer));
00255 DEBUG(grf, 0, "%s", buffer + 3);
00256
00257 SetDParam(1, engine);
00258 GetString(buffer, part2, lastof(buffer));
00259 DEBUG(grf, 0, "%s", buffer + 3);
00260 }
00261
00266 Vehicle::Vehicle(VehicleType type)
00267 {
00268 this->type = type;
00269 this->coord.left = INVALID_COORD;
00270 this->group_id = DEFAULT_GROUP;
00271 this->fill_percent_te_id = INVALID_TE_ID;
00272 this->first = this;
00273 this->colourmap = PAL_NONE;
00274 }
00275
00280 byte VehicleRandomBits()
00281 {
00282 return GB(Random(), 0, 8);
00283 }
00284
00285
00286
00287 const int HASH_BITS = 7;
00288 const int HASH_SIZE = 1 << HASH_BITS;
00289 const int HASH_MASK = HASH_SIZE - 1;
00290 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
00291 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
00292
00293
00294
00295 const int HASH_RES = 0;
00296
00297 static Vehicle *_new_vehicle_position_hash[TOTAL_HASH_SIZE];
00298
00299 static Vehicle *VehicleFromHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
00300 {
00301 for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
00302 for (int x = xl; ; x = (x + 1) & HASH_MASK) {
00303 Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00304 for (; v != NULL; v = v->next_new_hash) {
00305 Vehicle *a = proc(v, data);
00306 if (find_first && a != NULL) return a;
00307 }
00308 if (x == xu) break;
00309 }
00310 if (y == yu) break;
00311 }
00312
00313 return NULL;
00314 }
00315
00316
00328 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
00329 {
00330 const int COLL_DIST = 6;
00331
00332
00333 int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00334 int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00335 int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00336 int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00337
00338 return VehicleFromHash(xl, yl, xu, yu, data, proc, find_first);
00339 }
00340
00355 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00356 {
00357 VehicleFromPosXY(x, y, data, proc, false);
00358 }
00359
00371 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00372 {
00373 return VehicleFromPosXY(x, y, data, proc, true) != NULL;
00374 }
00375
00386 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
00387 {
00388 int x = GB(TileX(tile), HASH_RES, HASH_BITS);
00389 int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
00390
00391 Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00392 for (; v != NULL; v = v->next_new_hash) {
00393 if (v->tile != tile) continue;
00394
00395 Vehicle *a = proc(v, data);
00396 if (find_first && a != NULL) return a;
00397 }
00398
00399 return NULL;
00400 }
00401
00415 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00416 {
00417 VehicleFromPos(tile, data, proc, false);
00418 }
00419
00430 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00431 {
00432 return VehicleFromPos(tile, data, proc, true) != NULL;
00433 }
00434
00441 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
00442 {
00443 int32 z = *(int32*)data;
00444
00445 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
00446 if (v->z_pos > z) return NULL;
00447
00448 return v;
00449 }
00450
00456 CommandCost EnsureNoVehicleOnGround(TileIndex tile)
00457 {
00458 int32 z = GetTileMaxZ(tile);
00459
00460
00461
00462
00463
00464 Vehicle *v = VehicleFromPos(tile, &z, &EnsureNoVehicleProcZ, true);
00465 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00466 return CommandCost();
00467 }
00468
00470 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
00471 {
00472 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
00473 if (v == (const Vehicle *)data) return NULL;
00474
00475 return v;
00476 }
00477
00485 CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
00486 {
00487
00488
00489
00490
00491 Vehicle *v = VehicleFromPos(tile, (void *)ignore, &GetVehicleTunnelBridgeProc, true);
00492 if (v == NULL) v = VehicleFromPos(endtile, (void *)ignore, &GetVehicleTunnelBridgeProc, true);
00493
00494 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00495 return CommandCost();
00496 }
00497
00498 static Vehicle *EnsureNoTrainOnTrackProc(Vehicle *v, void *data)
00499 {
00500 TrackBits rail_bits = *(TrackBits *)data;
00501
00502 if (v->type != VEH_TRAIN) return NULL;
00503
00504 Train *t = Train::From(v);
00505 if ((t->track != rail_bits) && !TracksOverlap(t->track | rail_bits)) return NULL;
00506
00507 return v;
00508 }
00509
00518 CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
00519 {
00520
00521
00522
00523
00524 Vehicle *v = VehicleFromPos(tile, &track_bits, &EnsureNoTrainOnTrackProc, true);
00525 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00526 return CommandCost();
00527 }
00528
00529 static void UpdateNewVehiclePosHash(Vehicle *v, bool remove)
00530 {
00531 Vehicle **old_hash = v->old_new_hash;
00532 Vehicle **new_hash;
00533
00534 if (remove) {
00535 new_hash = NULL;
00536 } else {
00537 int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
00538 int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
00539 new_hash = &_new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00540 }
00541
00542 if (old_hash == new_hash) return;
00543
00544
00545 if (old_hash != NULL) {
00546 if (v->next_new_hash != NULL) v->next_new_hash->prev_new_hash = v->prev_new_hash;
00547 *v->prev_new_hash = v->next_new_hash;
00548 }
00549
00550
00551 if (new_hash != NULL) {
00552 v->next_new_hash = *new_hash;
00553 if (v->next_new_hash != NULL) v->next_new_hash->prev_new_hash = &v->next_new_hash;
00554 v->prev_new_hash = new_hash;
00555 *new_hash = v;
00556 }
00557
00558
00559 v->old_new_hash = new_hash;
00560 }
00561
00562 static Vehicle *_vehicle_position_hash[0x1000];
00563
00564 static void UpdateVehiclePosHash(Vehicle *v, int x, int y)
00565 {
00566 UpdateNewVehiclePosHash(v, x == INVALID_COORD);
00567
00568 Vehicle **old_hash, **new_hash;
00569 int old_x = v->coord.left;
00570 int old_y = v->coord.top;
00571
00572 new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(x, y)];
00573 old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(old_x, old_y)];
00574
00575 if (old_hash == new_hash) return;
00576
00577
00578 if (old_hash != NULL) {
00579 if (v->next_hash != NULL) v->next_hash->prev_hash = v->prev_hash;
00580 *v->prev_hash = v->next_hash;
00581 }
00582
00583
00584 if (new_hash != NULL) {
00585 v->next_hash = *new_hash;
00586 if (v->next_hash != NULL) v->next_hash->prev_hash = &v->next_hash;
00587 v->prev_hash = new_hash;
00588 *new_hash = v;
00589 }
00590 }
00591
00592 void ResetVehiclePosHash()
00593 {
00594 Vehicle *v;
00595 FOR_ALL_VEHICLES(v) { v->old_new_hash = NULL; }
00596 memset(_vehicle_position_hash, 0, sizeof(_vehicle_position_hash));
00597 memset(_new_vehicle_position_hash, 0, sizeof(_new_vehicle_position_hash));
00598 }
00599
00600 void ResetVehicleColourMap()
00601 {
00602 Vehicle *v;
00603 FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
00604 }
00605
00610 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
00611 static AutoreplaceMap _vehicles_to_autoreplace;
00612
00613 void InitializeVehicles()
00614 {
00615 _age_cargo_skip_counter = 1;
00616
00617 _vehicles_to_autoreplace.Reset();
00618 ResetVehiclePosHash();
00619 }
00620
00621 uint CountVehiclesInChain(const Vehicle *v)
00622 {
00623 uint count = 0;
00624 do count++; while ((v = v->Next()) != NULL);
00625 return count;
00626 }
00627
00633 void CountCompanyVehicles(CompanyID cid, uint counts[4])
00634 {
00635 for (uint i = 0; i < 4; i++) counts[i] = 0;
00636
00637 const Vehicle *v;
00638 FOR_ALL_VEHICLES(v) {
00639 if (v->owner == cid && v->IsPrimaryVehicle()) counts[v->type]++;
00640 }
00641 }
00642
00647 bool Vehicle::IsEngineCountable() const
00648 {
00649 switch (this->type) {
00650 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
00651 case VEH_TRAIN:
00652 return !this->IsArticulatedPart() &&
00653 !Train::From(this)->IsRearDualheaded();
00654 case VEH_ROAD: return RoadVehicle::From(this)->IsFrontEngine();
00655 case VEH_SHIP: return true;
00656 default: return false;
00657 }
00658 }
00659
00667 void Vehicle::HandlePathfindingResult(bool path_found)
00668 {
00669 if (path_found) {
00670
00671 if (!HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00672
00673
00674 ClrBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00675
00676 DeleteVehicleNews(this->index, STR_NEWS_VEHICLE_IS_LOST);
00677 return;
00678 }
00679
00680
00681 if (HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00682
00683
00684 SetBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00685
00686 AI::NewEvent(this->owner, new AIEventVehicleLost(this->index));
00687 if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) {
00688 SetDParam(0, this->index);
00689 AddVehicleNewsItem(STR_NEWS_VEHICLE_IS_LOST, NS_ADVICE, this->index);
00690 }
00691 }
00692
00694 void Vehicle::PreDestructor()
00695 {
00696 if (CleaningPool()) return;
00697
00698 if (Station::IsValidID(this->last_station_visited)) {
00699 Station *st = Station::Get(this->last_station_visited);
00700 st->loading_vehicles.remove(this);
00701
00702 HideFillingPercent(&this->fill_percent_te_id);
00703 this->CancelReservation(INVALID_STATION, st);
00704 delete this->cargo_payment;
00705 }
00706
00707 if (this->IsEngineCountable()) {
00708 Company::Get(this->owner)->num_engines[this->engine_type]--;
00709 if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
00710
00711 DeleteGroupHighlightOfVehicle(this);
00712 if (Group::IsValidID(this->group_id)) Group::Get(this->group_id)->num_engines[this->engine_type]--;
00713 if (this->IsPrimaryVehicle()) DecreaseGroupNumVehicle(this->group_id);
00714 }
00715
00716 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
00717 Aircraft *a = Aircraft::From(this);
00718 Station *st = GetTargetAirportIfValid(a);
00719 if (st != NULL) {
00720 const AirportFTA *layout = st->airport.GetFTA()->layout;
00721 CLRBITS(st->airport.flags, layout[a->previous_pos].block | layout[a->pos].block);
00722 }
00723 }
00724
00725
00726 if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
00727 RoadVehicle *v = RoadVehicle::From(this);
00728 if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
00729
00730 RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
00731 }
00732 }
00733
00734 if (this->Previous() == NULL) {
00735 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
00736 }
00737
00738 if (this->IsPrimaryVehicle()) {
00739 DeleteWindowById(WC_VEHICLE_VIEW, this->index);
00740 DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
00741 DeleteWindowById(WC_VEHICLE_REFIT, this->index);
00742 DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
00743 DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
00744 SetWindowDirty(WC_COMPANY, this->owner);
00745 OrderBackup::ClearVehicle(this);
00746 }
00747 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00748
00749 this->cargo.Truncate(0);
00750 DeleteVehicleOrders(this);
00751 DeleteDepotHighlightOfVehicle(this);
00752
00753 extern void StopGlobalFollowVehicle(const Vehicle *v);
00754 StopGlobalFollowVehicle(this);
00755
00756 ReleaseDisastersTargetingVehicle(this->index);
00757 }
00758
00759 Vehicle::~Vehicle()
00760 {
00761 free(this->name);
00762
00763 if (CleaningPool()) {
00764 this->cargo.OnCleanPool();
00765 return;
00766 }
00767
00768
00769
00770 if (this->IsDrawn()) MarkSingleVehicleDirty(this);
00771
00772 Vehicle *v = this->Next();
00773 this->SetNext(NULL);
00774
00775 delete v;
00776
00777 UpdateVehiclePosHash(this, INVALID_COORD, 0);
00778 DeleteVehicleNews(this->index, INVALID_STRING_ID);
00779 DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index);
00780 }
00781
00786 void VehicleEnteredDepotThisTick(Vehicle *v)
00787 {
00788
00789 _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
00790
00791
00792
00793
00794
00795
00796 v->vehstatus |= VS_STOPPED;
00797 }
00798
00804 static void RunVehicleDayProc()
00805 {
00806 if (_game_mode != GM_NORMAL) return;
00807
00808
00809 for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
00810 Vehicle *v = Vehicle::Get(i);
00811 if (v == NULL) continue;
00812
00813
00814 if ((v->day_counter & 0x1F) == 0) {
00815 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
00816 if (callback != CALLBACK_FAILED) {
00817 if (HasBit(callback, 0)) TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32);
00818 if (HasBit(callback, 1)) v->colourmap = PAL_NONE;
00819 }
00820 }
00821
00822
00823 v->OnNewDay();
00824 }
00825 }
00826
00827 void CallVehicleTicks()
00828 {
00829 _vehicles_to_autoreplace.Clear();
00830
00831 _age_cargo_skip_counter = (_age_cargo_skip_counter == 0) ? CARGO_AGING_TICKS - 1 : (_age_cargo_skip_counter - 1);
00832
00833 RunVehicleDayProc();
00834
00835 Station *st;
00836 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
00837
00838 Vehicle *v;
00839 FOR_ALL_VEHICLES(v) {
00840
00841 if (!v->Tick()) {
00842 assert(Vehicle::Get(vehicle_index) == NULL);
00843 continue;
00844 }
00845
00846 assert(Vehicle::Get(vehicle_index) == v);
00847
00848 switch (v->type) {
00849 default: break;
00850
00851 case VEH_TRAIN:
00852 if (HasBit(Train::From(v)->flags, VRF_TO_HEAVY)) {
00853 _current_company = v->owner;
00854 if (IsLocalCompany()) {
00855 SetDParam(0, v->index);
00856 SetDParam(1, STR_ERROR_TRAIN_TOO_HEAVY);
00857 AddVehicleNewsItem(STR_ERROR_TRAIN_TOO_HEAVY, NS_ADVICE, v->index);
00858 ClrBit(Train::From(v)->flags,VRF_TO_HEAVY);
00859 }
00860 _current_company = OWNER_NONE;
00861 }
00862 case VEH_ROAD:
00863 case VEH_AIRCRAFT:
00864 case VEH_SHIP:
00865 if (_age_cargo_skip_counter == 0) v->cargo.AgeCargo();
00866
00867 if (v->type == VEH_TRAIN && Train::From(v)->IsWagon()) continue;
00868 if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
00869 if (v->type == VEH_ROAD && !RoadVehicle::From(v)->IsFrontEngine()) continue;
00870
00871 v->motion_counter += v->cur_speed;
00872
00873 if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
00874
00875
00876 if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
00877 }
00878 }
00879
00880 Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
00881 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
00882 v = it->first;
00883
00884 cur_company.Change(v->owner);
00885
00886
00887
00888
00889 if (it->second) v->vehstatus &= ~VS_STOPPED;
00890
00891
00892 int x = v->x_pos;
00893 int y = v->y_pos;
00894 int z = v->z_pos;
00895
00896 const Company *c = Company::Get(_current_company);
00897 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
00898 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
00899 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
00900
00901 if (!IsLocalCompany()) continue;
00902
00903 if (res.Succeeded()) {
00904 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
00905 continue;
00906 }
00907
00908 StringID error_message = res.GetErrorMessage();
00909 if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
00910
00911 if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
00912
00913 StringID message;
00914 if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
00915 message = error_message;
00916 } else {
00917 message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
00918 }
00919
00920 SetDParam(0, v->index);
00921 SetDParam(1, error_message);
00922 AddVehicleNewsItem(message, NS_ADVICE, v->index);
00923 }
00924
00925 cur_company.Restore();
00926 }
00927
00932 static void DoDrawVehicle(const Vehicle *v)
00933 {
00934 SpriteID image = v->cur_image;
00935 PaletteID pal = PAL_NONE;
00936
00937 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
00938
00939
00940 bool shadowed = (v->vehstatus & (VS_SHADOW | VS_HIDDEN));
00941
00942 if (v->type == VEH_EFFECT) {
00943
00944
00945 TransparencyOption to = EffectVehicle::From(v)->GetTransparencyOption();
00946 if (to != TO_INVALID && (IsTransparencySet(to) || IsInvisibilitySet(to))) return;
00947 }
00948
00949 AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
00950 v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed);
00951 }
00952
00957 void ViewportAddVehicles(DrawPixelInfo *dpi)
00958 {
00959
00960 const int l = dpi->left;
00961 const int r = dpi->left + dpi->width;
00962 const int t = dpi->top;
00963 const int b = dpi->top + dpi->height;
00964
00965
00966 int xl, xu, yl, yu;
00967
00968 if (dpi->width + 70 < (1 << (7 + 6))) {
00969 xl = GB(l - 70, 7, 6);
00970 xu = GB(r, 7, 6);
00971 } else {
00972
00973 xl = 0;
00974 xu = 0x3F;
00975 }
00976
00977 if (dpi->height + 70 < (1 << (6 + 6))) {
00978 yl = GB(t - 70, 6, 6) << 6;
00979 yu = GB(b, 6, 6) << 6;
00980 } else {
00981
00982 yl = 0;
00983 yu = 0x3F << 6;
00984 }
00985
00986 for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
00987 for (int x = xl;; x = (x + 1) & 0x3F) {
00988 const Vehicle *v = _vehicle_position_hash[x + y];
00989
00990 while (v != NULL) {
00991 if (v->IsDrawn() &&
00992 l <= v->coord.right &&
00993 t <= v->coord.bottom &&
00994 r >= v->coord.left &&
00995 b >= v->coord.top) {
00996 DoDrawVehicle(v);
00997 }
00998 v = v->next_hash;
00999 }
01000
01001 if (x == xu) break;
01002 }
01003
01004 if (y == yu) break;
01005 }
01006 }
01007
01015 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
01016 {
01017 Vehicle *found = NULL, *v;
01018 uint dist, best_dist = UINT_MAX;
01019
01020 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
01021
01022 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
01023 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
01024
01025 FOR_ALL_VEHICLES(v) {
01026 if (v->IsDrawn() && !(v->vehstatus & VS_UNCLICKABLE) &&
01027 x >= v->coord.left && x <= v->coord.right &&
01028 y >= v->coord.top && y <= v->coord.bottom) {
01029
01030 dist = max(
01031 abs(((v->coord.left + v->coord.right) >> 1) - x),
01032 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
01033 );
01034
01035 if (dist < best_dist) {
01036 found = v;
01037 best_dist = dist;
01038 }
01039 }
01040 }
01041
01042 return found;
01043 }
01044
01049 void DecreaseVehicleValue(Vehicle *v)
01050 {
01051 v->value -= v->value >> 8;
01052 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01053 }
01054
01059 static const byte _breakdown_chances[4][4] = {
01060 {
01061 25,
01062 51,
01063 127,
01064 255,
01065 },
01066 {
01067 51,
01068 76,
01069 153,
01070 255,
01071 },
01072 {
01073 51,
01074 76,
01075 178,
01076 255,
01077 },
01078 {
01079 178,
01080 229,
01081 255,
01082 255,
01083 },
01084 };
01085
01092 void
01093 DetermineBreakdownType( Vehicle *v, uint32 r ) {
01094
01095 if ( !_settings_game.vehicle.improved_breakdowns) {
01096 v->breakdown_type = BREAKDOWN_CRITICAL;
01097 v->breakdown_severity = 40;
01098 return;
01099 }
01100
01101 byte rand = GB(r, 8, 8);
01102 const byte *breakdown_type_chance = _breakdown_chances[v->type];
01103
01104 if (v->type == VEH_AIRCRAFT) {
01105 if (rand <= breakdown_type_chance[BREAKDOWN_AIRCRAFT_SPEED]) {
01106 v->breakdown_type = BREAKDOWN_AIRCRAFT_SPEED;
01107
01108 byte max_speed = min(AircraftVehInfo(v->engine_type)->max_speed >> 3, 255);
01109 byte min_speed = min(15 + (max_speed >> 2), AircraftVehInfo( v->engine_type)->max_speed >> 4);
01110 v->breakdown_severity = min_speed + (((v->reliability + GB(r, 16, 16)) * (max_speed - min_speed)) >> 17);
01111 } else if (rand <= breakdown_type_chance[BREAKDOWN_AIRCRAFT_DEPOT]) {
01112 v->breakdown_type = BREAKDOWN_AIRCRAFT_DEPOT;
01113 } else if (rand <= breakdown_type_chance[BREAKDOWN_AIRCRAFT_EM_LANDING]) {
01114
01115 if (v->reliability < 0xDDDD) {
01116 v->breakdown_type = BREAKDOWN_AIRCRAFT_EM_LANDING;
01117 } else {
01118
01119 DetermineBreakdownType(v, Random());
01120 }
01121 } else {
01122 NOT_REACHED();
01123 }
01124 return;
01125 }
01126
01127 if (rand <= breakdown_type_chance[BREAKDOWN_CRITICAL]) {
01128 v->breakdown_type = BREAKDOWN_CRITICAL;
01129 } else if (rand <= breakdown_type_chance[BREAKDOWN_EM_STOP]) {
01130
01131 if (v->type == VEH_TRAIN && !(Train::From(v)->IsFrontEngine())) {
01132 return DetermineBreakdownType(v, Random());
01133 }
01134 v->breakdown_type = BREAKDOWN_EM_STOP;
01135 v->breakdown_delay >>= 2;
01136 } else if (rand <= breakdown_type_chance[BREAKDOWN_LOW_SPEED]) {
01137 v->breakdown_type = BREAKDOWN_LOW_SPEED;
01138
01139 uint16 rand2 = (GB(r, 16, 16) + v->reliability) >> 1;
01140 uint16 max_speed =
01141 (v->type == VEH_TRAIN) ? GetVehicleProperty(v, PROP_TRAIN_SPEED, RailVehInfo(v->engine_type)->max_speed) :
01142 (v->type == VEH_ROAD) ? GetVehicleProperty(v, PROP_ROADVEH_SPEED, RoadVehInfo(v->engine_type)->max_speed) :
01143 (v->type == VEH_SHIP) ? GetVehicleProperty(v,PROP_SHIP_SPEED, ShipVehInfo(v->engine_type)->max_speed) :
01144 GetVehicleProperty(v, PROP_AIRCRAFT_SPEED, AircraftVehInfo(v->engine_type)->max_speed);
01145 byte min_speed = min(41, max_speed >> 2);
01146
01147 max_speed = min(max_speed, 255);
01148 v->breakdown_severity = Clamp((max_speed * rand2) >> 16, min_speed, max_speed);
01149 } else if (rand <= breakdown_type_chance[BREAKDOWN_LOW_POWER]) {
01150 v->breakdown_type = BREAKDOWN_LOW_POWER;
01153 if (GB(r, 7, 1)) {
01154 v->breakdown_severity = Clamp((GB(r, 16, 16) + v->reliability) >> 9, 26, 231);
01155 } else {
01156 v->breakdown_severity = 0;
01157 }
01158 } else {
01159 NOT_REACHED();
01160 }
01161 }
01162
01163 void CheckVehicleBreakdown(Vehicle *v)
01164 {
01165 int rel, rel_old;
01166
01167
01168 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
01169 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->First()->index);
01170
01171 if (v->breakdown_ctr != 0 || (v->First()->vehstatus & VS_STOPPED) ||
01172 _settings_game.difficulty.vehicle_breakdowns < 1 ||
01173 v->First()->cur_speed < 5 || _game_mode == GM_MENU ||
01174 (v->type == VEH_AIRCRAFT && ((Aircraft*)v)->state != FLYING) ||
01175 (v->type == VEH_TRAIN && !(Train::From(v)->IsFrontEngine()) && !_settings_game.vehicle.improved_breakdowns)) {
01176 return;
01177 }
01178
01179 uint32 r1 = Random();
01180 uint32 r2 = Random();
01181
01182 byte chance = 128;
01183 if (_settings_game.vehicle.improved_breakdowns) {
01184
01185 chance = (v->type == VEH_TRAIN && Train::From(v)->IsMultiheaded()) ? v->First()->breakdown_chance * 7 / 10 : v->First()->breakdown_chance;
01186 } else if (v->type == VEH_SHIP) {
01187 chance = 64;
01188 }
01189
01198 if ((uint32) (0xffff - v->reliability) * _settings_game.difficulty.vehicle_breakdowns * chance > GB(r1, 0, 24) * 10) {
01199 v->breakdown_ctr = GB(r1, 24, 6) + 0xF;
01200 v->breakdown_delay = GB(r2, 0, 7) + 0x80;
01201 DetermineBreakdownType(v, r2);
01202 }
01203 }
01204
01211 bool Vehicle::HandleBreakdown()
01212 {
01213
01214
01215
01216
01217
01218 switch (this->breakdown_ctr) {
01219 case 0:
01220 return false;
01221
01222 case 2:
01223 this->breakdown_ctr = 1;
01224
01225 if (this->breakdowns_since_last_service != 255) {
01226 this->breakdowns_since_last_service++;
01227 }
01228
01229 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01230 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01231
01232 if (this->type == VEH_AIRCRAFT) {
01233 this->MarkDirty();
01234 assert(this->breakdown_type <= BREAKDOWN_AIRCRAFT_EM_LANDING);
01235
01236 this->vehstatus |= VS_AIRCRAFT_BROKEN;
01237 if(this->breakdown_type == BREAKDOWN_AIRCRAFT_SPEED ||
01238 (this->current_order.IsType(OT_GOTO_DEPOT) &&
01239 (this->current_order.GetDepotOrderType() & ODTFB_BREAKDOWN) &&
01240 GetTargetAirportIfValid(Aircraft::From(this)) != NULL)) {
01241 return false;
01242 }
01243 FindBreakdownDestination(Aircraft::From(this));
01244 } else if (this->type == VEH_TRAIN) {
01245 if (this->breakdown_type == BREAKDOWN_LOW_POWER ||
01246 this->First()->cur_speed <= ((this->breakdown_type == BREAKDOWN_LOW_SPEED) ? this->breakdown_severity : 0)) {
01247 switch ( this->breakdown_type ) {
01248 case BREAKDOWN_CRITICAL:
01249
01250 if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
01251 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
01252 (this->type == VEH_TRAIN ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
01253 (this->type == VEH_TRAIN ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
01254 }
01255
01256 if (!(this->vehstatus & VS_HIDDEN)) {
01257 EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
01258 if (u != NULL) u->animation_state = this->breakdown_delay * 2;
01259 }
01260
01261
01262 if (_settings_game.vehicle.improved_breakdowns) {
01263 if (!HasBit(Train::From(this)->flags,VRF_NEED_REPAIR)) {
01264 const Engine *e = Engine::Get(this->engine_type);
01265 const RailVehicleInfo *rvi = &e->u.rail;
01266 if (rvi->max_speed > this->vcache.cached_max_speed) this->vcache.cached_max_speed = rvi->max_speed;
01267 }
01268
01269 this->vcache.cached_max_speed =
01270 min(
01271 this->vcache.cached_max_speed - (this->vcache.cached_max_speed >> 1) / Train::From(this->First())->tcache.cached_num_engines + 1,
01272 this->vcache.cached_max_speed);
01273 SetBit(Train::From(this)->flags, VRF_NEED_REPAIR);
01274 Train::From(this->First())->ConsistChanged(true);
01275 }
01276
01277
01278 case BREAKDOWN_EM_STOP:
01279 CheckBreakdownFlags(Train::From(this->First()));
01280 SetBit(Train::From(this->First())->flags, VRF_BREAKDOWN_STOPPED);
01281 break;
01282
01283 case BREAKDOWN_LOW_SPEED:
01284 CheckBreakdownFlags(Train::From(this->First()));
01285 SetBit(Train::From(this->First())->flags, VRF_BREAKDOWN_SPEED);
01286 break;
01287
01288 case BREAKDOWN_LOW_POWER:
01289 SetBit(Train::From(this->First())->flags, VRF_BREAKDOWN_POWER);
01290 break;
01291 default: NOT_REACHED();
01292 }
01293
01294 this->First()->MarkDirty();
01295 SetWindowDirty(WC_VEHICLE_VIEW, this->First()->index);
01296 SetWindowDirty(WC_VEHICLE_DETAILS, this->First()->index);
01297 } else {
01298 this->breakdown_ctr = 2;
01299 this->breakdowns_since_last_service--;
01300 SetBit(Train::From(this)->flags, VRF_BREAKDOWN_BRAKING);
01301 return false;
01302 }
01303
01304 if ((!(this->vehstatus & VS_HIDDEN)) && ((this->breakdown_type == BREAKDOWN_LOW_SPEED || this->breakdown_type == BREAKDOWN_LOW_POWER) && (this->tick_counter & 0x1F) == 0)) {
01305 CreateEffectVehicleRel(this, 0, 0, 2, EV_CRASH_SMOKE);
01306 }
01307 } else {
01308 switch (this->breakdown_type) {
01309 case BREAKDOWN_CRITICAL:
01310 if (!PlayVehicleSound( this, VSE_BREAKDOWN)) {
01311 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ? SND_0F_VEHICLE_BREAKDOWN : SND_35_COMEDY_BREAKDOWN, this);
01312 }
01313
01314 if (!(this->vehstatus & VS_HIDDEN)) {
01315 EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
01316 if (u != NULL) u->animation_state = this->breakdown_delay * 2;
01317 }
01318
01319
01320 case BREAKDOWN_EM_STOP:
01321 this->cur_speed = 0;
01322 break;
01323
01324 case BREAKDOWN_LOW_SPEED:
01325 case BREAKDOWN_LOW_POWER:
01326
01327 break;
01328 default: NOT_REACHED();
01329 }
01330 if ((!(this->vehstatus & VS_HIDDEN)) &&
01331 (( this->breakdown_type == BREAKDOWN_LOW_SPEED || this->breakdown_type == BREAKDOWN_LOW_POWER) &&
01332 (this->tick_counter & 0x1F) == 0)) {
01333
01334 CreateEffectVehicleRel(this, 0, 0, 2, EV_CRASH_SMOKE);
01335 }
01336
01337 return (this->breakdown_type == BREAKDOWN_CRITICAL || this->breakdown_type == BREAKDOWN_EM_STOP);
01338 }
01339
01340 case 1:
01341
01342 if (this->type == VEH_AIRCRAFT) return false;
01343
01344
01345 if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
01346 if (--this->breakdown_delay == 0) {
01347 this->breakdown_ctr = 0;
01348 if(this->type == VEH_TRAIN) {
01349 CheckBreakdownFlags(Train::From(this->First()));
01350 this->First()->MarkDirty();
01351 SetWindowDirty(WC_VEHICLE_VIEW, this->First()->index);
01352 } else {
01353 this->MarkDirty();
01354 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01355 }
01356 }
01357 }
01358 return (this->breakdown_type == BREAKDOWN_CRITICAL || this->breakdown_type == BREAKDOWN_EM_STOP);
01359
01360 default:
01361 if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
01362 return false;
01363 }
01364 }
01365
01370 void AgeVehicle(Vehicle *v)
01371 {
01372 if (v->age < MAX_DAY) v->age++;
01373
01374 int age = v->age - v->max_age;
01375 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
01376 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
01377 v->reliability_spd_dec <<= 1;
01378 }
01379
01380 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01381
01382
01383 if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
01384
01385
01386 if (Company::Get(v->owner)->settings.engine_renew && Engine::Get(v->engine_type)->company_avail != 0) return;
01387
01388 StringID str;
01389 if (age == -DAYS_IN_LEAP_YEAR) {
01390 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
01391 } else if (age == 0) {
01392 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
01393 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
01394 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
01395 } else {
01396 return;
01397 }
01398
01399 SetDParam(0, v->index);
01400 AddVehicleNewsItem(str, NS_ADVICE, v->index);
01401 }
01402
01409 uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *colour)
01410 {
01411 int count = 0;
01412 int max = 0;
01413 int cars = 0;
01414 int unloading = 0;
01415 bool loading = false;
01416
01417 const Vehicle *u = v;
01418
01419 const Station *st = Station::GetIfValid(v->last_station_visited);
01420 assert(colour == NULL || st != NULL);
01421
01422
01423 for (; v != NULL; v = v->Next()) {
01424 count += v->cargo.OnboardCount();
01425 max += v->cargo_cap;
01426 if (v->cargo_cap != 0 && colour != NULL) {
01427 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
01428 loading |= !(u->current_order.GetLoadType() & OLFB_NO_LOAD) && st->goods[v->cargo_type].days_since_pickup != 255;
01429 cars++;
01430 }
01431 }
01432
01433 if (colour != NULL) {
01434 if (unloading == 0 && loading) {
01435 *colour = STR_PERCENT_UP;
01436 } else if (cars == unloading || !loading) {
01437 *colour = STR_PERCENT_DOWN;
01438 } else {
01439 *colour = STR_PERCENT_UP_DOWN;
01440 }
01441 }
01442
01443
01444 if (max == 0) return 100;
01445
01446
01447 return (count * 100) / max;
01448 }
01449
01454 void VehicleEnterDepot(Vehicle *v)
01455 {
01456
01457 assert(v == v->First());
01458
01459 switch (v->type) {
01460 case VEH_TRAIN: {
01461 Train *t = Train::From(v);
01462 SetWindowClassesDirty(WC_TRAINS_LIST);
01463
01464 SetDepotReservation(t->tile, false);
01465 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
01466
01467 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
01468 t->wait_counter = 0;
01469 t->force_proceed = TFP_NONE;
01470 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
01471 t->ConsistChanged(true);
01472 break;
01473 }
01474
01475 case VEH_ROAD:
01476 SetWindowClassesDirty(WC_ROADVEH_LIST);
01477 break;
01478
01479 case VEH_SHIP: {
01480 SetWindowClassesDirty(WC_SHIPS_LIST);
01481 Ship *ship = Ship::From(v);
01482 ship->state = TRACK_BIT_DEPOT;
01483 ship->UpdateCache();
01484 ship->UpdateViewport(true, true);
01485 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01486 break;
01487 }
01488
01489 case VEH_AIRCRAFT:
01490 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01491 HandleAircraftEnterHangar(Aircraft::From(v));
01492 break;
01493 default: NOT_REACHED();
01494 }
01495 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01496
01497 if (v->type != VEH_TRAIN) {
01498
01499
01500 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01501 }
01502 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01503
01504 v->vehstatus |= VS_HIDDEN;
01505 v->cur_speed = 0;
01506
01507 VehicleServiceInDepot(v);
01508
01509 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01510
01511 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01512 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01513
01514 const Order *real_order = v->GetOrder(v->cur_real_order_index);
01515 Order t = v->current_order;
01516 v->current_order.MakeDummy();
01517
01518
01519
01520 if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
01521 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
01522 (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
01523
01524 return;
01525 }
01526
01527 if (t.IsRefit()) {
01528 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01529 CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
01530 cur_company.Restore();
01531
01532 if (cost.Failed()) {
01533 _vehicles_to_autoreplace[v] = false;
01534 if (v->owner == _local_company) {
01535
01536 SetDParam(0, v->index);
01537 AddVehicleNewsItem(STR_NEWS_ORDER_REFIT_FAILED, NS_ADVICE, v->index);
01538 }
01539 } else if (cost.GetCost() != 0) {
01540 v->profit_this_year -= cost.GetCost() << 8;
01541 if (v->owner == _local_company) {
01542 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01543 }
01544 }
01545 }
01546
01547 if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01548
01549 v->DeleteUnreachedImplicitOrders();
01550 UpdateVehicleTimetable(v, true);
01551 v->IncrementImplicitOrderIndex();
01552 }
01553 if (t.GetDepotActionType() & ODATFB_HALT) {
01554
01555 if (_settings_game.order.timetable_separation) v->ClearSeparation();
01556 if (_settings_game.order.timetable_separation) ClrBit(v->vehicle_flags, VF_TIMETABLE_STARTED);
01557 _vehicles_to_autoreplace[v] = false;
01558 if (v->owner == _local_company) {
01559 SetDParam(0, v->index);
01560 AddVehicleNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, NS_ADVICE, v->index);
01561 }
01562 AI::NewEvent(v->owner, new AIEventVehicleWaitingInDepot(v->index));
01563 }
01564 }
01565 }
01566
01567
01575 void VehicleMove(Vehicle *v, bool update_viewport)
01576 {
01577 int img = v->cur_image;
01578 Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
01579 const Sprite *spr = GetSprite(img, ST_NORMAL);
01580
01581 pt.x += spr->x_offs;
01582 pt.y += spr->y_offs;
01583
01584 UpdateVehiclePosHash(v, pt.x, pt.y);
01585
01586 Rect old_coord = v->coord;
01587 v->coord.left = pt.x;
01588 v->coord.top = pt.y;
01589 v->coord.right = pt.x + spr->width + 2;
01590 v->coord.bottom = pt.y + spr->height + 2;
01591
01592 if (update_viewport) {
01593 MarkAllViewportsDirty(
01594 min(old_coord.left, v->coord.left),
01595 min(old_coord.top, v->coord.top),
01596 max(old_coord.right, v->coord.right) + 1,
01597 max(old_coord.bottom, v->coord.bottom) + 1
01598 );
01599 }
01600 }
01601
01610 void MarkSingleVehicleDirty(const Vehicle *v)
01611 {
01612 MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1, v->coord.bottom + 1);
01613 }
01614
01620 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01621 {
01622 static const int8 _delta_coord[16] = {
01623 -1,-1,-1, 0, 1, 1, 1, 0,
01624 -1, 0, 1, 1, 1, 0,-1,-1,
01625 };
01626
01627 int x = v->x_pos + _delta_coord[v->direction];
01628 int y = v->y_pos + _delta_coord[v->direction + 8];
01629
01630 GetNewVehiclePosResult gp;
01631 gp.x = x;
01632 gp.y = y;
01633 gp.old_tile = v->tile;
01634 gp.new_tile = TileVirtXY(x, y);
01635 return gp;
01636 }
01637
01638 static const Direction _new_direction_table[] = {
01639 DIR_N, DIR_NW, DIR_W,
01640 DIR_NE, DIR_SE, DIR_SW,
01641 DIR_E, DIR_SE, DIR_S
01642 };
01643
01644 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01645 {
01646 int i = 0;
01647
01648 if (y >= v->y_pos) {
01649 if (y != v->y_pos) i += 3;
01650 i += 3;
01651 }
01652
01653 if (x >= v->x_pos) {
01654 if (x != v->x_pos) i++;
01655 i++;
01656 }
01657
01658 Direction dir = v->direction;
01659
01660 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01661 if (dirdiff == DIRDIFF_SAME) return dir;
01662 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01663 }
01664
01674 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01675 {
01676 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01677 }
01678
01686 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01687 {
01688
01689 const Vehicle *v;
01690 FOR_ALL_VEHICLES(v) {
01691 if (v->type == type && v->owner == owner) {
01692 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01693 }
01694 }
01695
01696 if (this->maxid == 0) return;
01697
01698
01699
01700
01701 this->cache = CallocT<bool>(this->maxid + 2);
01702
01703
01704 FOR_ALL_VEHICLES(v) {
01705 if (v->type == type && v->owner == owner) {
01706 this->cache[v->unitnumber] = true;
01707 }
01708 }
01709 }
01710
01712 UnitID FreeUnitIDGenerator::NextID()
01713 {
01714 if (this->maxid <= this->curid) return ++this->curid;
01715
01716 while (this->cache[++this->curid]) { }
01717
01718 return this->curid;
01719 }
01720
01726 UnitID GetFreeUnitNumber(VehicleType type)
01727 {
01728
01729 uint max_veh;
01730 switch (type) {
01731 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
01732 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
01733 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
01734 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
01735 default: NOT_REACHED();
01736 }
01737
01738 uint amounts[4];
01739 CountCompanyVehicles(_current_company, amounts);
01740 assert((uint)type < lengthof(amounts));
01741 if (amounts[type] >= max_veh) return UINT16_MAX;
01742
01743 FreeUnitIDGenerator gen(type, _current_company);
01744
01745 return gen.NextID();
01746 }
01747
01748
01757 bool CanBuildVehicleInfrastructure(VehicleType type)
01758 {
01759 assert(IsCompanyBuildableVehicleType(type));
01760
01761 if (!Company::IsValidID(_local_company)) return false;
01762 if (!_settings_client.gui.disable_unsuitable_building) return true;
01763
01764 UnitID max;
01765 switch (type) {
01766 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
01767 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
01768 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
01769 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01770 default: NOT_REACHED();
01771 }
01772
01773
01774 if (max > 0) {
01775
01776 const Engine *e;
01777 FOR_ALL_ENGINES_OF_TYPE(e, type) {
01778 if (HasBit(e->company_avail, _local_company)) return true;
01779 }
01780 return false;
01781 }
01782
01783
01784 const Vehicle *v;
01785 FOR_ALL_VEHICLES(v) {
01786 if (v->owner == _local_company && v->type == type) return true;
01787 }
01788
01789 return false;
01790 }
01791
01792
01800 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
01801 {
01802 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01803 const Engine *e = Engine::Get(engine_type);
01804 switch (e->type) {
01805 default: NOT_REACHED();
01806 case VEH_TRAIN:
01807 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
01808
01809
01810 engine_type = parent_engine_type;
01811 e = Engine::Get(engine_type);
01812
01813 }
01814
01815 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01816 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01817 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
01818 if (!CargoSpec::Get(cargo_type)->is_freight) {
01819 if (parent_engine_type == INVALID_ENGINE) {
01820 return LS_PASSENGER_WAGON_STEAM;
01821 } else {
01822 switch (RailVehInfo(parent_engine_type)->engclass) {
01823 default: NOT_REACHED();
01824 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
01825 case EC_DIESEL: return LS_PASSENGER_WAGON_DIESEL;
01826 case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
01827 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
01828 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
01829 }
01830 }
01831 } else {
01832 return LS_FREIGHT_WAGON;
01833 }
01834 } else {
01835 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
01836
01837 switch (e->u.rail.engclass) {
01838 default: NOT_REACHED();
01839 case EC_STEAM: return LS_STEAM;
01840 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
01841 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
01842 case EC_MONORAIL: return LS_MONORAIL;
01843 case EC_MAGLEV: return LS_MAGLEV;
01844 }
01845 }
01846
01847 case VEH_ROAD:
01848
01849 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01850 engine_type = parent_engine_type;
01851 e = Engine::Get(engine_type);
01852 cargo_type = v->First()->cargo_type;
01853 }
01854 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01855 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01856
01857
01858 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
01859
01860 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01861 } else {
01862
01863 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01864 }
01865
01866 case VEH_SHIP:
01867 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01868 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01869 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01870
01871 case VEH_AIRCRAFT:
01872 switch (e->u.air.subtype) {
01873 case AIR_HELI: return LS_HELICOPTER;
01874 case AIR_CTOL: return LS_SMALL_PLANE;
01875 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
01876 default: NOT_REACHED();
01877 }
01878 }
01879 }
01880
01890 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
01891 {
01892 const Company *c = Company::Get(company);
01893 LiveryScheme scheme = LS_DEFAULT;
01894
01895
01896
01897 if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
01898
01899 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
01900
01901
01902 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01903 }
01904
01905 return &c->livery[scheme];
01906 }
01907
01908
01909 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01910 {
01911 PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01912
01913
01914 if (map != PAL_NONE) return map;
01915
01916 const Engine *e = Engine::Get(engine_type);
01917
01918
01919 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
01920 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01921
01922 if (callback != CALLBACK_FAILED) {
01923 assert_compile(PAL_NONE == 0);
01924 map = GB(callback, 0, 14);
01925
01926
01927 if (!HasBit(callback, 14)) {
01928
01929 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01930 return map;
01931 }
01932 }
01933 }
01934
01935 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
01936
01937 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
01938
01939
01940 if (!Company::IsValidID(company)) return map;
01941
01942 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
01943
01944 map += livery->colour1;
01945 if (twocc) map += livery->colour2 * 16;
01946
01947
01948 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01949 return map;
01950 }
01951
01958 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
01959 {
01960 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01961 }
01962
01968 PaletteID GetVehiclePalette(const Vehicle *v)
01969 {
01970 if (v->IsGroundVehicle()) {
01971 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
01972 }
01973
01974 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01975 }
01976
01985 uint GetVehicleCapacity(const Vehicle *v, uint16 *mail_capacity)
01986 {
01987 if (mail_capacity != NULL) *mail_capacity = 0;
01988 const Engine *e = Engine::Get(v->engine_type);
01989
01990 if (!e->CanCarryCargo()) return 0;
01991
01992 if (mail_capacity != NULL && e->type == VEH_AIRCRAFT && IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
01993 *mail_capacity = GetVehicleProperty(v, PROP_AIRCRAFT_MAIL_CAPACITY, e->u.air.mail_capacity);
01994 }
01995 CargoID default_cargo = e->GetDefaultCargoType();
01996
01997
01998
01999 if (HasBit(e->info.callback_mask, CBM_VEHICLE_REFIT_CAPACITY) &&
02000 (default_cargo != v->cargo_type || v->cargo_subtype != 0)) {
02001 uint16 callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
02002 if (callback != CALLBACK_FAILED) return callback;
02003 }
02004
02005
02006 uint capacity;
02007 switch (e->type) {
02008 case VEH_TRAIN: capacity = GetVehicleProperty(v, PROP_TRAIN_CARGO_CAPACITY, e->u.rail.capacity); break;
02009 case VEH_ROAD: capacity = GetVehicleProperty(v, PROP_ROADVEH_CARGO_CAPACITY, e->u.road.capacity); break;
02010 case VEH_SHIP: capacity = GetVehicleProperty(v, PROP_SHIP_CARGO_CAPACITY, e->u.ship.capacity); break;
02011 case VEH_AIRCRAFT: capacity = GetVehicleProperty(v, PROP_AIRCRAFT_PASSENGER_CAPACITY, e->u.air.passenger_capacity); break;
02012 default: NOT_REACHED();
02013 }
02014
02015
02016
02017 if (e->type != VEH_SHIP) {
02018 if (e->type == VEH_AIRCRAFT) {
02019 if (!IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
02020 capacity += GetVehicleProperty(v, PROP_AIRCRAFT_MAIL_CAPACITY, e->u.air.mail_capacity);
02021 }
02022 if (v->cargo_type == CT_MAIL) return capacity;
02023 } else {
02024 switch (default_cargo) {
02025 case CT_PASSENGERS: break;
02026 case CT_MAIL:
02027 case CT_GOODS: capacity *= 2; break;
02028 default: capacity *= 4; break;
02029 }
02030 }
02031 switch (v->cargo_type) {
02032 case CT_PASSENGERS: break;
02033 case CT_MAIL:
02034 case CT_GOODS: capacity /= 2; break;
02035 default: capacity /= 4; break;
02036 }
02037 }
02038
02039 return capacity;
02040 }
02041
02045 void Vehicle::DeleteUnreachedImplicitOrders()
02046 {
02047 if (this->IsGroundVehicle()) {
02048 uint16 &gv_flags = this->GetGroundVehicleFlags();
02049 if (HasBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)) {
02050
02051 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02052 this->cur_implicit_order_index = this->cur_real_order_index;
02053 InvalidateVehicleOrder(this, 0);
02054 return;
02055 }
02056 }
02057
02058 const Order *order = this->GetOrder(this->cur_implicit_order_index);
02059 while (order != NULL) {
02060 if (this->cur_implicit_order_index == this->cur_real_order_index) break;
02061
02062 if (order->IsType(OT_IMPLICIT)) {
02063
02064 order = order->next;
02065 DeleteOrder(this, this->cur_implicit_order_index);
02066 } else {
02067
02068 order = order->next;
02069 this->cur_implicit_order_index++;
02070 }
02071
02072
02073 if (order == NULL) {
02074 order = this->GetOrder(0);
02075 this->cur_implicit_order_index = 0;
02076 }
02077 }
02078 }
02079
02084 void Vehicle::BeginLoading()
02085 {
02086 assert(IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP);
02087
02088 if (this->current_order.IsType(OT_GOTO_STATION) &&
02089 this->current_order.GetDestination() == this->last_station_visited) {
02090 this->DeleteUnreachedImplicitOrders();
02091
02092
02093 this->current_order.MakeLoading(true);
02094 UpdateVehicleTimetable(this, true);
02095
02096
02097
02098
02099
02100
02101 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
02102
02103 } else {
02104
02105
02106
02107
02108
02109 Order *in_list = this->GetOrder(this->cur_implicit_order_index);
02110 if (this->IsGroundVehicle() && in_list != NULL &&
02111 (!in_list->IsType(OT_IMPLICIT) ||
02112 in_list->GetDestination() != this->last_station_visited)) {
02113 bool suppress_implicit_orders = HasBit(this->GetGroundVehicleFlags(), GVF_SUPPRESS_IMPLICIT_ORDERS);
02114
02115 Order *prev_order = this->cur_implicit_order_index > 0 ? this->GetOrder(this->cur_implicit_order_index - 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : NULL);
02116 if (prev_order == NULL ||
02117 (!prev_order->IsType(OT_IMPLICIT) && !prev_order->IsType(OT_GOTO_STATION)) ||
02118 prev_order->GetDestination() != this->last_station_visited) {
02119
02120
02121
02122 int target_index = this->cur_implicit_order_index;
02123 bool found = false;
02124 while (target_index != this->cur_real_order_index) {
02125 const Order *order = this->GetOrder(target_index);
02126 if (order->IsType(OT_IMPLICIT) && order->GetDestination() == this->last_station_visited) {
02127 found = true;
02128 break;
02129 }
02130 target_index++;
02131 if (target_index >= this->orders.list->GetNumOrders()) target_index = 0;
02132 assert(target_index != this->cur_implicit_order_index);
02133 }
02134
02135 if (found) {
02136 if (suppress_implicit_orders) {
02137
02138 this->cur_implicit_order_index = target_index;
02139 InvalidateVehicleOrder(this, 0);
02140 } else {
02141
02142 const Order *order = this->GetOrder(this->cur_implicit_order_index);
02143 while (!order->IsType(OT_IMPLICIT) || order->GetDestination() != this->last_station_visited) {
02144 if (order->IsType(OT_IMPLICIT)) {
02145
02146 order = order->next;
02147 DeleteOrder(this, this->cur_implicit_order_index);
02148 } else {
02149
02150 order = order->next;
02151 this->cur_implicit_order_index++;
02152 }
02153
02154
02155 if (order == NULL) {
02156 order = this->GetOrder(0);
02157 this->cur_implicit_order_index = 0;
02158 }
02159 assert(order != NULL);
02160 }
02161 }
02162 } else if (!suppress_implicit_orders && this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID && Order::CanAllocateItem()) {
02163
02164 Order *implicit_order = new Order();
02165 implicit_order->MakeImplicit(this->last_station_visited);
02166 InsertOrder(this, implicit_order, this->cur_implicit_order_index);
02167 if (this->cur_implicit_order_index > 0) --this->cur_implicit_order_index;
02168
02169
02170
02171 uint16 &gv_flags = this->GetGroundVehicleFlags();
02172 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02173 }
02174 }
02175 }
02176 this->current_order.MakeLoading(false);
02177 }
02178
02179 Station *curr_station = Station::Get(this->last_station_visited);
02180 curr_station->loading_vehicles.push_back(this);
02181
02182 StationID next_station_id = INVALID_STATION;
02183 OrderList *orders = this->orders.list;
02184 if (orders != NULL) {
02185 next_station_id = orders->GetNextStoppingStation(this->cur_implicit_order_index, this->last_station_visited);
02186 }
02187
02188 if (this->last_loading_station != INVALID_STATION && this->last_loading_station != this->last_station_visited) {
02189 IncreaseStats(Station::Get(this->last_loading_station), this, this->last_station_visited, false);
02190 }
02191
02192 if (this->CanLeaveWithCargo() && next_station_id != INVALID_STATION) {
02193 assert(next_station_id != this->last_station_visited);
02194
02195 IncreaseStats(curr_station, this, next_station_id, true);
02196 }
02197
02198 PrepareUnload(curr_station, this, next_station_id);
02199
02200 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
02201 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
02202 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
02203 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
02204
02205 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
02206 this->cur_speed = 0;
02207 this->MarkDirty();
02208 }
02209
02214 void Vehicle::CancelReservation(StationID next, Station *st)
02215 {
02216 for (Vehicle *v = this; v != NULL; v = v->next) {
02217 VehicleCargoList &cargo = v->cargo;
02218 if (cargo.ReservedCount() > 0) {
02219 DEBUG(misc, 1, "cancelling cargo reservation");
02220 GoodsEntry &ge = st->goods[v->cargo_type];
02221 cargo.Unreserve(next, &ge.cargo);
02222 SetBit(ge.acceptance_pickup, GoodsEntry::GES_PICKUP);
02223 }
02224 }
02225 }
02226
02233 bool Vehicle::CanLeaveWithCargo()
02234 {
02235 return (this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
02236 ((this->current_order.GetUnloadType() & (OUFB_UNLOAD | OUFB_TRANSFER)) == 0 &&
02237 this->last_loading_station != INVALID_STATION);
02238 }
02239
02244 void Vehicle::LeaveStation()
02245 {
02246 assert(this->current_order.IsType(OT_LOADING));
02247
02248 delete this->cargo_payment;
02249
02250
02251 if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
02252
02253 this->current_order.MakeLeaveStation();
02254 Station *st = Station::Get(this->last_station_visited);
02255 st->loading_vehicles.remove(this);
02256
02257 OrderList *orders = this->orders.list;
02258 if (orders != NULL) {
02259 StationID next_station_id = orders->GetNextStoppingStation(this->cur_implicit_order_index, this->last_station_visited);
02260 this->CancelReservation(next_station_id, st);
02261 if (next_station_id != INVALID_STATION && next_station_id != this->last_station_visited) {
02262 DecreaseFrozen(st, this, next_station_id);
02263 }
02264 } else {
02265 this->CancelReservation(INVALID_STATION, st);
02266 DEBUG(misc, 1, "orders are NULL");
02267 RecalcFrozen(st);
02268 }
02269
02270 if (this->CanLeaveWithCargo()) {
02271
02272 this->last_loading_station = this->last_station_visited;
02273 } else {
02274
02275
02276
02277 this->last_loading_station = INVALID_STATION;
02278 }
02279
02280 HideFillingPercent(&this->fill_percent_te_id);
02281
02282 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
02283
02284 if (IsTileType(this->tile, MP_STATION)) TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
02285
02286 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
02287 }
02288 }
02289
02290
02296 void Vehicle::HandleLoading(bool mode)
02297 {
02298 switch (this->current_order.GetType()) {
02299 case OT_LOADING: {
02300 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
02301
02302
02303 if (_settings_game.order.timetabling && !HasBit(this->vehicle_flags, VF_LOADING_FINISHED))
02304 this->current_loading_time = this->current_order_time;
02305
02306
02307 if (!mode && this->type != VEH_TRAIN) PayStationSharingFee(this, Station::Get(this->last_station_visited));
02308
02309
02310 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) ||
02311 (_settings_game.order.timetabling && this->current_order_time < wait_time)) return;
02312
02313 this->PlayLeaveStationSound();
02314
02315 this->LeaveStation();
02316
02317
02318 const Order *order = this->GetOrder(this->cur_implicit_order_index);
02319 if (order == NULL ||
02320 (!order->IsType(OT_IMPLICIT) && !order->IsType(OT_GOTO_STATION)) ||
02321 order->GetDestination() != this->last_station_visited) {
02322 return;
02323 }
02324 break;
02325 }
02326
02327 case OT_DUMMY: break;
02328
02329 default: return;
02330 }
02331
02332 this->IncrementImplicitOrderIndex();
02333 }
02334
02341 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
02342 {
02343 CommandCost ret = CheckOwnership(this->owner);
02344 if (ret.Failed()) return ret;
02345
02346 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
02347 if (this->IsStoppedInDepot()) return CMD_ERROR;
02348
02349 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
02350 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
02351 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
02352
02353
02354
02355 if (flags & DC_EXEC) {
02356 if (!(this->current_order.GetDepotOrderType() & ODTFB_BREAKDOWN)) this->current_order.SetDepotOrderType(ODTF_MANUAL);
02357 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
02358 if (_settings_game.order.timetable_separation) this->ClearSeparation();
02359 if (_settings_game.order.timetable_separation) ClrBit(this->vehicle_flags, VF_TIMETABLE_STARTED);
02360 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
02361 }
02362 return CommandCost();
02363 }
02364
02365 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR;
02366 if (flags & DC_EXEC) {
02367
02368
02369 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
02370
02371 if (this->IsGroundVehicle()) {
02372 uint16 &gv_flags = this->GetGroundVehicleFlags();
02373 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02374 }
02375
02376
02377 if (this->current_order.GetDepotOrderType() & ODTFB_BREAKDOWN) {
02378 this->current_order.SetDepotActionType(this->current_order.GetDepotActionType() == ODATFB_HALT ? ODATF_SERVICE_ONLY : ODATFB_HALT);
02379 } else {
02380 this->current_order.MakeDummy();
02381 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
02382 }
02383 }
02384 return CommandCost();
02385 }
02386
02387 TileIndex location;
02388 DestinationID destination;
02389 bool reverse;
02390 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};
02391 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
02392
02393 if (flags & DC_EXEC) {
02394 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
02395
02396 if (_settings_game.order.timetable_separation) this->ClearSeparation();
02397 if (_settings_game.order.timetable_separation) ClrBit(this->vehicle_flags, VF_TIMETABLE_STARTED);
02398
02399 if (this->IsGroundVehicle()) {
02400 uint16 &gv_flags = this->GetGroundVehicleFlags();
02401 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02402 }
02403
02404 this->dest_tile = location;
02405 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
02406 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
02407 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
02408
02409
02410 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
02411
02412 if (this->type == VEH_AIRCRAFT) {
02413 Aircraft *a = Aircraft::From(this);
02414 if (a->state == FLYING && a->targetairport != destination) {
02415
02416 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
02417 AircraftNextAirportPos_and_Order(a);
02418 }
02419 }
02420 }
02421
02422 return CommandCost();
02423
02424 }
02425
02430 void Vehicle::UpdateVisualEffect(bool allow_power_change)
02431 {
02432 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02433 const Engine *e = Engine::Get(this->engine_type);
02434
02435
02436 byte visual_effect;
02437 switch (e->type) {
02438 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
02439 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
02440 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
02441 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
02442 }
02443
02444
02445 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
02446 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
02447
02448 if (callback != CALLBACK_FAILED) {
02449 callback = GB(callback, 0, 8);
02450
02451
02452 if (callback == VE_DEFAULT) {
02453 assert(HasBit(callback, VE_DISABLE_EFFECT));
02454 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
02455 }
02456 visual_effect = callback;
02457 }
02458 }
02459
02460
02461 if (visual_effect == VE_DEFAULT ||
02462 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
02463
02464
02465 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
02466 if (visual_effect == VE_DEFAULT) {
02467 visual_effect = 1 << VE_DISABLE_EFFECT;
02468 } else {
02469 SetBit(visual_effect, VE_DISABLE_EFFECT);
02470 }
02471 } else {
02472 if (visual_effect == VE_DEFAULT) {
02473
02474 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
02475 }
02476 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
02477 }
02478 }
02479
02480 this->vcache.cached_vis_effect = visual_effect;
02481
02482 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
02483 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02484 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
02485 }
02486 }
02487
02488 static const int8 _vehicle_smoke_pos[8] = {
02489 1, 1, 1, 0, -1, -1, -1, 0
02490 };
02491
02496 void Vehicle::ShowVisualEffect() const
02497 {
02498 assert(this->IsPrimaryVehicle());
02499 bool sound = false;
02500
02501
02502
02503
02504
02505
02506 if (_settings_game.vehicle.smoke_amount == 0 ||
02507 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
02508 this->cur_speed < 2) {
02509 return;
02510 }
02511 if (this->type == VEH_TRAIN) {
02512 const Train *t = Train::From(this);
02513
02514
02515
02516
02517 if (HasBit(t->flags, VRF_REVERSING) ||
02518 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
02519 t->cur_speed >= t->Train::GetCurrentMaxSpeed())) {
02520 return;
02521 }
02522 }
02523
02524 const Vehicle *v = this;
02525
02526 do {
02527 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
02528 byte effect_type = GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
02529 bool disable_effect = HasBit(v->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
02530
02531
02532
02533
02534
02535
02536
02537
02538 if (disable_effect ||
02539 v->vehstatus & VS_HIDDEN ||
02540 (MayHaveBridgeAbove(v->tile) && IsBridgeAbove(v->tile)) ||
02541 IsDepotTile(v->tile) ||
02542 IsTunnelTile(v->tile) ||
02543 (v->type == VEH_TRAIN &&
02544 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
02545 continue;
02546 }
02547
02548 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
02549 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
02550
02551 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
02552 x = -x;
02553 y = -y;
02554 }
02555
02556 switch (effect_type) {
02557 case VE_TYPE_STEAM:
02558
02559
02560
02561
02562
02563 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / this->vcache.cached_max_speed))) == 0) {
02564 CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
02565 sound = true;
02566 }
02567 break;
02568
02569 case VE_TYPE_DIESEL: {
02570
02571
02572
02573
02574
02575
02576
02577
02578
02579
02580
02581 int power_weight_effect = 0;
02582 if (v->type == VEH_TRAIN) {
02583 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
02584 }
02585 if (this->cur_speed < (this->vcache.cached_max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
02586 Chance16((64 - ((this->cur_speed << 5) / this->vcache.cached_max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
02587 CreateEffectVehicleRel(v, x, y, 10, EV_DIESEL_SMOKE);
02588 sound = true;
02589 }
02590 break;
02591 }
02592
02593 case VE_TYPE_ELECTRIC:
02594
02595
02596
02597
02598
02599
02600 if (GB(v->tick_counter, 0, 2) == 0 &&
02601 Chance16((6 - ((this->cur_speed << 2) / this->vcache.cached_max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
02602 CreateEffectVehicleRel(v, x, y, 10, EV_ELECTRIC_SPARK);
02603 sound = true;
02604 }
02605 break;
02606
02607 default:
02608 break;
02609 }
02610 } while ((v = v->Next()) != NULL);
02611
02612 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
02613 }
02614
02619 void Vehicle::SetNext(Vehicle *next)
02620 {
02621 assert(this != next);
02622
02623 if (this->next != NULL) {
02624
02625 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02626 v->first = this->next;
02627 }
02628 this->next->previous = NULL;
02629 }
02630
02631 this->next = next;
02632
02633 if (this->next != NULL) {
02634
02635 if (this->next->previous != NULL) this->next->previous->next = NULL;
02636 this->next->previous = this;
02637 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02638 v->first = this->first;
02639 }
02640 }
02641 }
02642
02644 void Vehicle::ClearSeparation()
02645 {
02646 if (this->ahead_separation == NULL && this->behind_separation == NULL) return;
02647
02648 assert(this->ahead_separation != NULL);
02649 assert(this->behind_separation != NULL);
02650
02651 this->ahead_separation->behind_separation = this->behind_separation;
02652 this->behind_separation->ahead_separation = this->ahead_separation;
02653
02654 this->ahead_separation = NULL;
02655 this->behind_separation = NULL;
02656
02657 SetWindowDirty(WC_VEHICLE_TIMETABLE, this->index);
02658 }
02659
02665 void Vehicle::InitSeparation()
02666 {
02667 assert(this->ahead_separation == NULL && this->behind_separation == NULL);
02668 Vehicle *best_match = this;
02669 int lowest_separation;
02670 for (Vehicle *v_other = this->FirstShared(); v_other != NULL; v_other = v_other->NextShared()) {
02671 if ((HasBit(v_other->vehicle_flags, VF_TIMETABLE_STARTED)) && v_other != this) {
02672 if (best_match == this) {
02673 best_match = v_other;
02674 lowest_separation = 0;
02675 } else {
02676 int temp_sep = 0;
02677 if (temp_sep < lowest_separation && temp_sep != -1) {
02678 best_match = v_other;
02679 lowest_separation = temp_sep;
02680 }
02681 }
02682 }
02683 }
02684 this->AddToSeparationBehind(best_match);
02685 }
02686
02692 void Vehicle::AddToSeparationBehind(Vehicle *v_other)
02693 {
02694 if (v_other->ahead_separation == NULL) v_other->ahead_separation = v_other;
02695 if (v_other->behind_separation == NULL) v_other->behind_separation = v_other;
02696
02697 this->ahead_separation = v_other;
02698 v_other->behind_separation->ahead_separation = this;
02699 this->behind_separation = v_other->behind_separation;
02700 v_other->behind_separation = this;
02701 }
02702
02708 void Vehicle::AddToShared(Vehicle *shared_chain)
02709 {
02710 assert(this->previous_shared == NULL && this->next_shared == NULL);
02711
02712 if (shared_chain->orders.list == NULL) {
02713 assert(shared_chain->previous_shared == NULL);
02714 assert(shared_chain->next_shared == NULL);
02715 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
02716 }
02717
02718 this->next_shared = shared_chain->next_shared;
02719 this->previous_shared = shared_chain;
02720
02721 shared_chain->next_shared = this;
02722
02723 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
02724
02725 shared_chain->orders.list->AddVehicle(this);
02726 }
02727
02731 void Vehicle::RemoveFromShared()
02732 {
02733
02734
02735 bool were_first = (this->FirstShared() == this);
02736 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
02737
02738 this->orders.list->RemoveVehicle(this);
02739
02740 if (!were_first) {
02741
02742 this->previous_shared->next_shared = this->NextShared();
02743 }
02744
02745 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
02746
02747
02748 if (this->orders.list->GetNumVehicles() == 1) {
02749
02750 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
02751 InvalidateVehicleOrder(this->FirstShared(), 0);
02752 } else if (were_first) {
02753
02754
02755 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
02756 }
02757
02758 this->next_shared = NULL;
02759 this->previous_shared = NULL;
02760
02761 if (_settings_game.order.timetable_separation) this->ClearSeparation();
02762 if (_settings_game.order.timetable_separation) ClrBit(this->vehicle_flags, VF_TIMETABLE_STARTED);
02763 }
02764
02765 void VehiclesYearlyLoop()
02766 {
02767 Vehicle *v;
02768 FOR_ALL_VEHICLES(v) {
02769 if (v->IsPrimaryVehicle()) {
02770
02771 Money profit = v->GetDisplayProfitThisYear();
02772 if (v->age >= 730 && profit < 0) {
02773 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
02774 SetDParam(0, v->index);
02775 SetDParam(1, profit);
02776 AddVehicleNewsItem(
02777 STR_NEWS_VEHICLE_IS_UNPROFITABLE,
02778 NS_ADVICE,
02779 v->index
02780 );
02781 }
02782 AI::NewEvent(v->owner, new AIEventVehicleUnprofitable(v->index));
02783 }
02784
02785 v->profit_last_year = v->profit_this_year;
02786 v->profit_this_year = 0;
02787 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
02788 }
02789 }
02790 }
02791
02792
02802 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
02803 {
02804 const Engine *e = Engine::GetIfValid(engine_type);
02805 assert(e != NULL);
02806
02807 switch (e->type) {
02808 case VEH_TRAIN:
02809 return (st->facilities & FACIL_TRAIN) != 0;
02810
02811 case VEH_ROAD:
02812
02813
02814
02815 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
02816
02817 case VEH_SHIP:
02818 return (st->facilities & FACIL_DOCK) != 0;
02819
02820 case VEH_AIRCRAFT:
02821 return (st->facilities & FACIL_AIRPORT) != 0 &&
02822 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
02823
02824 default:
02825 return false;
02826 }
02827 }
02828
02835 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
02836 {
02837 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
02838
02839 return CanVehicleUseStation(v->engine_type, st);
02840 }
02841
02847 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
02848 {
02849 assert(this->IsGroundVehicle());
02850 if (this->type == VEH_TRAIN) {
02851 return &Train::From(this)->gcache;
02852 } else {
02853 return &RoadVehicle::From(this)->gcache;
02854 }
02855 }
02856
02862 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
02863 {
02864 assert(this->IsGroundVehicle());
02865 if (this->type == VEH_TRAIN) {
02866 return &Train::From(this)->gcache;
02867 } else {
02868 return &RoadVehicle::From(this)->gcache;
02869 }
02870 }
02871
02877 uint16 &Vehicle::GetGroundVehicleFlags()
02878 {
02879 assert(this->IsGroundVehicle());
02880 if (this->type == VEH_TRAIN) {
02881 return Train::From(this)->gv_flags;
02882 } else {
02883 return RoadVehicle::From(this)->gv_flags;
02884 }
02885 }
02886
02892 const uint16 &Vehicle::GetGroundVehicleFlags() const
02893 {
02894 assert(this->IsGroundVehicle());
02895 if (this->type == VEH_TRAIN) {
02896 return Train::From(this)->gv_flags;
02897 } else {
02898 return RoadVehicle::From(this)->gv_flags;
02899 }
02900 }
02901
02910 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
02911 {
02912 if (v->type == VEH_TRAIN) {
02913 Train *u = Train::From(v);
02914
02915 u = u->GetFirstEnginePart();
02916
02917
02918 for (; u != NULL && num_vehicles > 0; num_vehicles--) {
02919 do {
02920
02921 set.Include(u->index);
02922
02923
02924 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
02925
02926 u = u->Next();
02927 } while (u != NULL && u->IsArticulatedPart());
02928 }
02929 }
02930 }