00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include "gui.h"
00014 #include "roadveh.h"
00015 #include "ship.h"
00016 #include "spritecache.h"
00017 #include "timetable.h"
00018 #include "viewport_func.h"
00019 #include "news_func.h"
00020 #include "command_func.h"
00021 #include "company_func.h"
00022 #include "vehicle_gui.h"
00023 #include "train.h"
00024 #include "aircraft.h"
00025 #include "newgrf_debug.h"
00026 #include "newgrf_sound.h"
00027 #include "newgrf_station.h"
00028 #include "group.h"
00029 #include "group_gui.h"
00030 #include "strings_func.h"
00031 #include "zoom_func.h"
00032 #include "functions.h"
00033 #include "date_func.h"
00034 #include "window_func.h"
00035 #include "vehicle_func.h"
00036 #include "autoreplace_func.h"
00037 #include "autoreplace_gui.h"
00038 #include "station_base.h"
00039 #include "ai/ai.hpp"
00040 #include "depot_func.h"
00041 #include "network/network.h"
00042 #include "core/pool_func.hpp"
00043 #include "economy_base.h"
00044 #include "articulated_vehicles.h"
00045 #include "roadstop_base.h"
00046 #include "core/random_func.hpp"
00047 #include "core/backup_type.hpp"
00048 #include "order_backup.h"
00049 #include "sound_func.h"
00050 #include "effectvehicle_func.h"
00051 #include "effectvehicle_base.h"
00052 #include "vehiclelist.h"
00053 #include "tunnel_map.h"
00054 #include "depot_map.h"
00055 #include "ground_vehicle.hpp"
00056
00057 #include "table/strings.h"
00058
00059 #define GEN_HASH(x, y) ((GB((y), 6, 6) << 6) + GB((x), 7, 6))
00060
00061 VehicleID _vehicle_id_ctr_day;
00062 VehicleID _new_vehicle_id;
00063 uint16 _returned_refit_capacity;
00064 uint16 _returned_mail_refit_capacity;
00065 byte _age_cargo_skip_counter;
00066
00067
00068
00069 VehiclePool _vehicle_pool("Vehicle");
00070 INSTANTIATE_POOL_METHODS(Vehicle)
00071
00072
00077 bool Vehicle::NeedsAutorenewing(const Company *c) const
00078 {
00079
00080
00081
00082
00083 assert(c == Company::Get(this->owner));
00084
00085 if (!c->settings.engine_renew) return false;
00086 if (this->age - this->max_age < (c->settings.engine_renew_months * 30)) return false;
00087 if (this->age == 0) return false;
00088
00089 return true;
00090 }
00091
00092 void VehicleServiceInDepot(Vehicle *v)
00093 {
00094 v->date_of_last_service = _date;
00095 v->breakdowns_since_last_service = 0;
00096 v->reliability = Engine::Get(v->engine_type)->reliability;
00097 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
00098 }
00099
00100 bool Vehicle::NeedsServicing() const
00101 {
00102
00103
00104 if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
00105
00106
00107 const Company *c = Company::Get(this->owner);
00108 if (c->settings.vehicle.servint_ispercent ?
00109 (this->reliability >= Engine::Get(this->engine_type)->reliability * (100 - this->service_interval) / 100) :
00110 (this->date_of_last_service + this->service_interval >= _date)) {
00111 return false;
00112 }
00113
00114
00115
00116 if (!_settings_game.order.no_servicing_if_no_breakdowns ||
00117 _settings_game.difficulty.vehicle_breakdowns != 0) {
00118 return true;
00119 }
00120
00121
00122
00123
00124 bool pending_replace = false;
00125 Money needed_money = c->settings.engine_renew_money;
00126 if (needed_money > c->money) return false;
00127
00128 for (const Vehicle *v = this; v != NULL; v = (v->type == VEH_TRAIN) ? Train::From(v)->GetNextUnit() : NULL) {
00129 EngineID new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id);
00130
00131
00132 if (new_engine == INVALID_ENGINE || !HasBit(Engine::Get(new_engine)->company_avail, v->owner)) continue;
00133
00134
00135 uint32 available_cargo_types, union_mask;
00136 GetArticulatedRefitMasks(new_engine, true, &union_mask, &available_cargo_types);
00137
00138 if (union_mask != 0) {
00139 CargoID cargo_type;
00140
00141 if (IsArticulatedVehicleCarryingDifferentCargos(v, &cargo_type)) continue;
00142
00143
00144 if (cargo_type != CT_INVALID) {
00145
00146 if (!HasBit(available_cargo_types, cargo_type)) continue;
00147 }
00148 }
00149
00150
00151
00152 pending_replace = true;
00153 needed_money += 2 * Engine::Get(new_engine)->GetCost();
00154 if (needed_money > c->money) return false;
00155 }
00156
00157 return pending_replace;
00158 }
00159
00160 bool Vehicle::NeedsAutomaticServicing() const
00161 {
00162 if (_settings_game.order.gotodepot && this->HasDepotOrder()) return false;
00163 if (this->current_order.IsType(OT_LOADING)) return false;
00164 if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
00165 return NeedsServicing();
00166 }
00167
00168 uint Vehicle::Crash(bool flooded)
00169 {
00170 assert((this->vehstatus & VS_CRASHED) == 0);
00171 assert(this->Previous() == NULL);
00172
00173 uint pass = 0;
00174
00175 if (this->IsPrimaryVehicle()) this->vehstatus |= VS_STOPPED;
00176
00177 for (Vehicle *v = this; v != NULL; v = v->Next()) {
00178 if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo.Count();
00179 v->vehstatus |= VS_CRASHED;
00180 MarkSingleVehicleDirty(v);
00181 }
00182
00183
00184 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00185 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
00186 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
00187 SetWindowDirty(WC_VEHICLE_DEPOT, this->tile);
00188
00189 return pass;
00190 }
00191
00192
00201 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
00202 {
00203 const Engine *e = Engine::Get(engine);
00204 uint32 grfid = e->grf_prop.grffile->grfid;
00205 GRFConfig *grfconfig = GetGRFConfig(grfid);
00206
00207 if (!HasBit(grfconfig->grf_bugs, bug_type)) {
00208 SetBit(grfconfig->grf_bugs, bug_type);
00209 SetDParamStr(0, grfconfig->GetName());
00210 SetDParam(1, engine);
00211 ShowErrorMessage(part1, part2, WL_CRITICAL);
00212 if (!_networking) DoCommand(0, critical ? PM_PAUSED_ERROR : PM_PAUSED_NORMAL, 1, DC_EXEC, CMD_PAUSE);
00213 }
00214
00215
00216 char buffer[512];
00217
00218 SetDParamStr(0, grfconfig->GetName());
00219 GetString(buffer, part1, lastof(buffer));
00220 DEBUG(grf, 0, "%s", buffer + 3);
00221
00222 SetDParam(1, engine);
00223 GetString(buffer, part2, lastof(buffer));
00224 DEBUG(grf, 0, "%s", buffer + 3);
00225 }
00226
00231 Vehicle::Vehicle(VehicleType type)
00232 {
00233 this->type = type;
00234 this->coord.left = INVALID_COORD;
00235 this->group_id = DEFAULT_GROUP;
00236 this->fill_percent_te_id = INVALID_TE_ID;
00237 this->first = this;
00238 this->colourmap = PAL_NONE;
00239 }
00240
00245 byte VehicleRandomBits()
00246 {
00247 return GB(Random(), 0, 8);
00248 }
00249
00250
00251
00252 const int HASH_BITS = 7;
00253 const int HASH_SIZE = 1 << HASH_BITS;
00254 const int HASH_MASK = HASH_SIZE - 1;
00255 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
00256 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
00257
00258
00259
00260 const int HASH_RES = 0;
00261
00262 static Vehicle *_new_vehicle_position_hash[TOTAL_HASH_SIZE];
00263
00264 static Vehicle *VehicleFromHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
00265 {
00266 for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
00267 for (int x = xl; ; x = (x + 1) & HASH_MASK) {
00268 Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00269 for (; v != NULL; v = v->next_new_hash) {
00270 Vehicle *a = proc(v, data);
00271 if (find_first && a != NULL) return a;
00272 }
00273 if (x == xu) break;
00274 }
00275 if (y == yu) break;
00276 }
00277
00278 return NULL;
00279 }
00280
00281
00293 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
00294 {
00295 const int COLL_DIST = 6;
00296
00297
00298 int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00299 int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00300 int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00301 int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00302
00303 return VehicleFromHash(xl, yl, xu, yu, data, proc, find_first);
00304 }
00305
00320 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00321 {
00322 VehicleFromPosXY(x, y, data, proc, false);
00323 }
00324
00336 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00337 {
00338 return VehicleFromPosXY(x, y, data, proc, true) != NULL;
00339 }
00340
00351 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
00352 {
00353 int x = GB(TileX(tile), HASH_RES, HASH_BITS);
00354 int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
00355
00356 Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00357 for (; v != NULL; v = v->next_new_hash) {
00358 if (v->tile != tile) continue;
00359
00360 Vehicle *a = proc(v, data);
00361 if (find_first && a != NULL) return a;
00362 }
00363
00364 return NULL;
00365 }
00366
00380 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00381 {
00382 VehicleFromPos(tile, data, proc, false);
00383 }
00384
00395 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00396 {
00397 return VehicleFromPos(tile, data, proc, true) != NULL;
00398 }
00399
00406 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
00407 {
00408 byte z = *(byte*)data;
00409
00410 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
00411 if (v->z_pos > z) return NULL;
00412
00413 return v;
00414 }
00415
00421 CommandCost EnsureNoVehicleOnGround(TileIndex tile)
00422 {
00423 byte z = GetTileMaxZ(tile);
00424
00425
00426
00427
00428
00429 Vehicle *v = VehicleFromPos(tile, &z, &EnsureNoVehicleProcZ, true);
00430 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00431 return CommandCost();
00432 }
00433
00435 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
00436 {
00437 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
00438 if (v == (const Vehicle *)data) return NULL;
00439
00440 return v;
00441 }
00442
00450 CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
00451 {
00452
00453
00454
00455
00456 Vehicle *v = VehicleFromPos(tile, (void *)ignore, &GetVehicleTunnelBridgeProc, true);
00457 if (v == NULL) v = VehicleFromPos(endtile, (void *)ignore, &GetVehicleTunnelBridgeProc, true);
00458
00459 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00460 return CommandCost();
00461 }
00462
00463 static Vehicle *EnsureNoTrainOnTrackProc(Vehicle *v, void *data)
00464 {
00465 TrackBits rail_bits = *(TrackBits *)data;
00466
00467 if (v->type != VEH_TRAIN) return NULL;
00468
00469 Train *t = Train::From(v);
00470 if ((t->track != rail_bits) && !TracksOverlap(t->track | rail_bits)) return NULL;
00471
00472 return v;
00473 }
00474
00483 CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
00484 {
00485
00486
00487
00488
00489 Vehicle *v = VehicleFromPos(tile, &track_bits, &EnsureNoTrainOnTrackProc, true);
00490 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00491 return CommandCost();
00492 }
00493
00494 static void UpdateNewVehiclePosHash(Vehicle *v, bool remove)
00495 {
00496 Vehicle **old_hash = v->old_new_hash;
00497 Vehicle **new_hash;
00498
00499 if (remove) {
00500 new_hash = NULL;
00501 } else {
00502 int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
00503 int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
00504 new_hash = &_new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00505 }
00506
00507 if (old_hash == new_hash) return;
00508
00509
00510 if (old_hash != NULL) {
00511 if (v->next_new_hash != NULL) v->next_new_hash->prev_new_hash = v->prev_new_hash;
00512 *v->prev_new_hash = v->next_new_hash;
00513 }
00514
00515
00516 if (new_hash != NULL) {
00517 v->next_new_hash = *new_hash;
00518 if (v->next_new_hash != NULL) v->next_new_hash->prev_new_hash = &v->next_new_hash;
00519 v->prev_new_hash = new_hash;
00520 *new_hash = v;
00521 }
00522
00523
00524 v->old_new_hash = new_hash;
00525 }
00526
00527 static Vehicle *_vehicle_position_hash[0x1000];
00528
00529 static void UpdateVehiclePosHash(Vehicle *v, int x, int y)
00530 {
00531 UpdateNewVehiclePosHash(v, x == INVALID_COORD);
00532
00533 Vehicle **old_hash, **new_hash;
00534 int old_x = v->coord.left;
00535 int old_y = v->coord.top;
00536
00537 new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(x, y)];
00538 old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(old_x, old_y)];
00539
00540 if (old_hash == new_hash) return;
00541
00542
00543 if (old_hash != NULL) {
00544 if (v->next_hash != NULL) v->next_hash->prev_hash = v->prev_hash;
00545 *v->prev_hash = v->next_hash;
00546 }
00547
00548
00549 if (new_hash != NULL) {
00550 v->next_hash = *new_hash;
00551 if (v->next_hash != NULL) v->next_hash->prev_hash = &v->next_hash;
00552 v->prev_hash = new_hash;
00553 *new_hash = v;
00554 }
00555 }
00556
00557 void ResetVehiclePosHash()
00558 {
00559 Vehicle *v;
00560 FOR_ALL_VEHICLES(v) { v->old_new_hash = NULL; }
00561 memset(_vehicle_position_hash, 0, sizeof(_vehicle_position_hash));
00562 memset(_new_vehicle_position_hash, 0, sizeof(_new_vehicle_position_hash));
00563 }
00564
00565 void ResetVehicleColourMap()
00566 {
00567 Vehicle *v;
00568 FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
00569 }
00570
00575 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
00576 static AutoreplaceMap _vehicles_to_autoreplace;
00577
00578 void InitializeVehicles()
00579 {
00580 _vehicle_pool.CleanPool();
00581 _cargo_payment_pool.CleanPool();
00582
00583 _age_cargo_skip_counter = 1;
00584
00585 _vehicles_to_autoreplace.Reset();
00586 ResetVehiclePosHash();
00587 }
00588
00589 uint CountVehiclesInChain(const Vehicle *v)
00590 {
00591 uint count = 0;
00592 do count++; while ((v = v->Next()) != NULL);
00593 return count;
00594 }
00595
00601 void CountCompanyVehicles(CompanyID cid, uint counts[4])
00602 {
00603 for (uint i = 0; i < 4; i++) counts[i] = 0;
00604
00605 const Vehicle *v;
00606 FOR_ALL_VEHICLES(v) {
00607 if (v->owner == cid && v->IsPrimaryVehicle()) counts[v->type]++;
00608 }
00609 }
00610
00615 bool Vehicle::IsEngineCountable() const
00616 {
00617 switch (this->type) {
00618 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
00619 case VEH_TRAIN:
00620 return !Train::From(this)->IsArticulatedPart() &&
00621 !Train::From(this)->IsRearDualheaded();
00622 case VEH_ROAD: return RoadVehicle::From(this)->IsRoadVehFront();
00623 case VEH_SHIP: return true;
00624 default: return false;
00625 }
00626 }
00627
00635 void Vehicle::HandlePathfindingResult(bool path_found)
00636 {
00637 if (path_found) {
00638
00639 if (!HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00640
00641
00642 ClrBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00643
00644 DeleteVehicleNews(this->index, STR_NEWS_VEHICLE_IS_LOST);
00645 return;
00646 }
00647
00648
00649 if (HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00650
00651
00652 SetBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00653
00654 AI::NewEvent(this->owner, new AIEventVehicleLost(this->index));
00655 if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) {
00656 SetDParam(0, this->index);
00657 AddVehicleNewsItem(STR_NEWS_VEHICLE_IS_LOST, NS_ADVICE, this->index);
00658 }
00659 }
00660
00661 void Vehicle::PreDestructor()
00662 {
00663 if (CleaningPool()) return;
00664
00665 if (Station::IsValidID(this->last_station_visited)) {
00666 Station::Get(this->last_station_visited)->loading_vehicles.remove(this);
00667
00668 HideFillingPercent(&this->fill_percent_te_id);
00669
00670 delete this->cargo_payment;
00671 }
00672
00673 if (this->IsEngineCountable()) {
00674 Company::Get(this->owner)->num_engines[this->engine_type]--;
00675 if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
00676
00677 DeleteGroupHighlightOfVehicle(this);
00678 if (Group::IsValidID(this->group_id)) Group::Get(this->group_id)->num_engines[this->engine_type]--;
00679 if (this->IsPrimaryVehicle()) DecreaseGroupNumVehicle(this->group_id);
00680 }
00681
00682 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
00683 Aircraft *a = Aircraft::From(this);
00684 Station *st = GetTargetAirportIfValid(a);
00685 if (st != NULL) {
00686 const AirportFTA *layout = st->airport.GetFTA()->layout;
00687 CLRBITS(st->airport.flags, layout[a->previous_pos].block | layout[a->pos].block);
00688 }
00689 }
00690
00691
00692 if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
00693 RoadVehicle *v = RoadVehicle::From(this);
00694 if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
00695
00696 RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
00697 }
00698 }
00699
00700 if (this->Previous() == NULL) {
00701 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
00702 }
00703
00704 if (this->IsPrimaryVehicle()) {
00705 DeleteWindowById(WC_VEHICLE_VIEW, this->index);
00706 DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
00707 DeleteWindowById(WC_VEHICLE_REFIT, this->index);
00708 DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
00709 DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
00710 SetWindowDirty(WC_COMPANY, this->owner);
00711 OrderBackup::ClearVehicle(this);
00712 }
00713 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00714
00715 this->cargo.Truncate(0);
00716 DeleteVehicleOrders(this);
00717 DeleteDepotHighlightOfVehicle(this);
00718
00719 extern void StopGlobalFollowVehicle(const Vehicle *v);
00720 StopGlobalFollowVehicle(this);
00721
00722 ReleaseDisastersTargetingVehicle(this->index);
00723 }
00724
00725 Vehicle::~Vehicle()
00726 {
00727 free(this->name);
00728
00729 if (CleaningPool()) return;
00730
00731
00732
00733 if (!(this->vehstatus & VS_HIDDEN)) MarkSingleVehicleDirty(this);
00734
00735 Vehicle *v = this->Next();
00736 this->SetNext(NULL);
00737
00738 delete v;
00739
00740 UpdateVehiclePosHash(this, INVALID_COORD, 0);
00741 DeleteVehicleNews(this->index, INVALID_STRING_ID);
00742 DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index);
00743 }
00744
00749 void VehicleEnteredDepotThisTick(Vehicle *v)
00750 {
00751
00752 _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
00753
00754
00755
00756
00757
00758
00759 v->vehstatus |= VS_STOPPED;
00760 }
00761
00767 static void RunVehicleDayProc()
00768 {
00769 if (_game_mode != GM_NORMAL) return;
00770
00771
00772 for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
00773 Vehicle *v = Vehicle::Get(i);
00774 if (v == NULL) continue;
00775
00776
00777 if ((v->day_counter & 0x1F) == 0) {
00778 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
00779 if (callback != CALLBACK_FAILED) {
00780 if (HasBit(callback, 0)) TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32);
00781 if (HasBit(callback, 1)) v->colourmap = PAL_NONE;
00782 }
00783 }
00784
00785
00786 v->OnNewDay();
00787 }
00788 }
00789
00790 void CallVehicleTicks()
00791 {
00792 _vehicles_to_autoreplace.Clear();
00793
00794 _age_cargo_skip_counter = (_age_cargo_skip_counter == 0) ? 184 : (_age_cargo_skip_counter - 1);
00795
00796 RunVehicleDayProc();
00797
00798 Station *st;
00799 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
00800
00801 Vehicle *v;
00802 FOR_ALL_VEHICLES(v) {
00803
00804 if (!v->Tick()) {
00805 assert(Vehicle::Get(vehicle_index) == NULL);
00806 continue;
00807 }
00808
00809 assert(Vehicle::Get(vehicle_index) == v);
00810
00811 switch (v->type) {
00812 default: break;
00813
00814 case VEH_TRAIN:
00815 case VEH_ROAD:
00816 case VEH_AIRCRAFT:
00817 case VEH_SHIP:
00818 if (_age_cargo_skip_counter == 0) v->cargo.AgeCargo();
00819
00820 if (v->type == VEH_TRAIN && Train::From(v)->IsWagon()) continue;
00821 if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
00822 if (v->type == VEH_ROAD && !RoadVehicle::From(v)->IsRoadVehFront()) continue;
00823
00824 v->motion_counter += v->cur_speed;
00825
00826 if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
00827
00828
00829 if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
00830 }
00831 }
00832
00833 Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
00834 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
00835 v = it->first;
00836
00837 cur_company.Change(v->owner);
00838
00839
00840
00841
00842 if (it->second) v->vehstatus &= ~VS_STOPPED;
00843
00844
00845 int x = v->x_pos;
00846 int y = v->y_pos;
00847 int z = v->z_pos;
00848
00849 const Company *c = Company::Get(_current_company);
00850 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
00851 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
00852 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
00853
00854 if (!IsLocalCompany()) continue;
00855
00856 if (res.Succeeded()) {
00857 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
00858 continue;
00859 }
00860
00861 StringID error_message = res.GetErrorMessage();
00862 if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
00863
00864 if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
00865
00866 StringID message;
00867 if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
00868 message = error_message;
00869 } else {
00870 message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
00871 }
00872
00873 SetDParam(0, v->index);
00874 SetDParam(1, error_message);
00875 AddVehicleNewsItem(message, NS_ADVICE, v->index);
00876 }
00877
00878 cur_company.Restore();
00879 }
00880
00885 static void DoDrawVehicle(const Vehicle *v)
00886 {
00887 SpriteID image = v->cur_image;
00888 PaletteID pal = PAL_NONE;
00889
00890 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
00891
00892 AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
00893 v->x_extent, v->y_extent, v->z_extent, v->z_pos, (v->vehstatus & VS_SHADOW) != 0);
00894 }
00895
00896 void ViewportAddVehicles(DrawPixelInfo *dpi)
00897 {
00898
00899 const int l = dpi->left;
00900 const int r = dpi->left + dpi->width;
00901 const int t = dpi->top;
00902 const int b = dpi->top + dpi->height;
00903
00904
00905 int xl, xu, yl, yu;
00906
00907 if (dpi->width + 70 < (1 << (7 + 6))) {
00908 xl = GB(l - 70, 7, 6);
00909 xu = GB(r, 7, 6);
00910 } else {
00911
00912 xl = 0;
00913 xu = 0x3F;
00914 }
00915
00916 if (dpi->height + 70 < (1 << (6 + 6))) {
00917 yl = GB(t - 70, 6, 6) << 6;
00918 yu = GB(b, 6, 6) << 6;
00919 } else {
00920
00921 yl = 0;
00922 yu = 0x3F << 6;
00923 }
00924
00925 for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
00926 for (int x = xl;; x = (x + 1) & 0x3F) {
00927 const Vehicle *v = _vehicle_position_hash[x + y];
00928
00929 while (v != NULL) {
00930 if (!(v->vehstatus & VS_HIDDEN) &&
00931 l <= v->coord.right &&
00932 t <= v->coord.bottom &&
00933 r >= v->coord.left &&
00934 b >= v->coord.top) {
00935 DoDrawVehicle(v);
00936 }
00937 v = v->next_hash;
00938 }
00939
00940 if (x == xu) break;
00941 }
00942
00943 if (y == yu) break;
00944 }
00945 }
00946
00947 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
00948 {
00949 Vehicle *found = NULL, *v;
00950 uint dist, best_dist = UINT_MAX;
00951
00952 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
00953
00954 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
00955 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
00956
00957 FOR_ALL_VEHICLES(v) {
00958 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
00959 x >= v->coord.left && x <= v->coord.right &&
00960 y >= v->coord.top && y <= v->coord.bottom) {
00961
00962 dist = max(
00963 abs(((v->coord.left + v->coord.right) >> 1) - x),
00964 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
00965 );
00966
00967 if (dist < best_dist) {
00968 found = v;
00969 best_dist = dist;
00970 }
00971 }
00972 }
00973
00974 return found;
00975 }
00976
00977 void DecreaseVehicleValue(Vehicle *v)
00978 {
00979 v->value -= v->value >> 8;
00980 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
00981 }
00982
00983 static const byte _breakdown_chance[64] = {
00984 3, 3, 3, 3, 3, 3, 3, 3,
00985 4, 4, 5, 5, 6, 6, 7, 7,
00986 8, 8, 9, 9, 10, 10, 11, 11,
00987 12, 13, 13, 13, 13, 14, 15, 16,
00988 17, 19, 21, 25, 28, 31, 34, 37,
00989 40, 44, 48, 52, 56, 60, 64, 68,
00990 72, 80, 90, 100, 110, 120, 130, 140,
00991 150, 170, 190, 210, 230, 250, 250, 250,
00992 };
00993
00994 void CheckVehicleBreakdown(Vehicle *v)
00995 {
00996 int rel, rel_old;
00997
00998
00999 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
01000 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01001
01002 if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
01003 _settings_game.difficulty.vehicle_breakdowns < 1 ||
01004 v->cur_speed < 5 || _game_mode == GM_MENU) {
01005 return;
01006 }
01007
01008 uint32 r = Random();
01009
01010
01011 int chance = v->breakdown_chance + 1;
01012 if (Chance16I(1, 25, r)) chance += 25;
01013 v->breakdown_chance = min(255, chance);
01014
01015
01016 rel = v->reliability;
01017 if (v->type == VEH_SHIP) rel += 0x6666;
01018
01019
01020 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
01021
01022
01023 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
01024 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
01025 v->breakdown_delay = GB(r, 24, 7) + 0x80;
01026 v->breakdown_chance = 0;
01027 }
01028 }
01029
01030 bool Vehicle::HandleBreakdown()
01031 {
01032
01033
01034
01035
01036
01037 switch (this->breakdown_ctr) {
01038 case 0:
01039 return false;
01040
01041 case 2:
01042 this->breakdown_ctr = 1;
01043
01044 if (this->breakdowns_since_last_service != 255) {
01045 this->breakdowns_since_last_service++;
01046 }
01047
01048 this->MarkDirty();
01049 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01050 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01051
01052 if (this->type == VEH_AIRCRAFT) {
01053
01054 this->vehstatus |= VS_AIRCRAFT_BROKEN;
01055 } else {
01056 this->cur_speed = 0;
01057
01058 if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
01059 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
01060 (this->type == VEH_TRAIN ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
01061 (this->type == VEH_TRAIN ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
01062 }
01063
01064 if (!(this->vehstatus & VS_HIDDEN)) {
01065 EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
01066 if (u != NULL) u->animation_state = this->breakdown_delay * 2;
01067 }
01068 }
01069
01070 case 1:
01071
01072 if (this->type == VEH_AIRCRAFT) return false;
01073
01074
01075 if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
01076 if (--this->breakdown_delay == 0) {
01077 this->breakdown_ctr = 0;
01078 this->MarkDirty();
01079 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01080 }
01081 }
01082 return true;
01083
01084 default:
01085 if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
01086 return false;
01087 }
01088 }
01089
01094 void AgeVehicle(Vehicle *v)
01095 {
01096 if (v->age < MAX_DAY) v->age++;
01097
01098 int age = v->age - v->max_age;
01099 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
01100 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
01101 v->reliability_spd_dec <<= 1;
01102 }
01103
01104 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01105
01106
01107 if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
01108
01109
01110 if (Company::Get(v->owner)->settings.engine_renew && Engine::Get(v->engine_type)->company_avail != 0) return;
01111
01112 StringID str;
01113 if (age == -DAYS_IN_LEAP_YEAR) {
01114 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
01115 } else if (age == 0) {
01116 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
01117 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
01118 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
01119 } else {
01120 return;
01121 }
01122
01123 SetDParam(0, v->index);
01124 AddVehicleNewsItem(str, NS_ADVICE, v->index);
01125 }
01126
01133 uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *colour)
01134 {
01135 int count = 0;
01136 int max = 0;
01137 int cars = 0;
01138 int unloading = 0;
01139 bool loading = false;
01140
01141 const Vehicle *u = v;
01142
01143 const Station *st = Station::GetIfValid(v->last_station_visited);
01144 assert(colour == NULL || st != NULL);
01145
01146
01147 for (; v != NULL; v = v->Next()) {
01148 count += v->cargo.Count();
01149 max += v->cargo_cap;
01150 if (v->cargo_cap != 0 && colour != NULL) {
01151 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
01152 loading |= !(u->current_order.GetLoadType() & OLFB_NO_LOAD) && st->goods[v->cargo_type].days_since_pickup != 255;
01153 cars++;
01154 }
01155 }
01156
01157 if (colour != NULL) {
01158 if (unloading == 0 && loading) {
01159 *colour = STR_PERCENT_UP;
01160 } else if (cars == unloading || !loading) {
01161 *colour = STR_PERCENT_DOWN;
01162 } else {
01163 *colour = STR_PERCENT_UP_DOWN;
01164 }
01165 }
01166
01167
01168 if (max == 0) return 100;
01169
01170
01171 return (count * 100) / max;
01172 }
01173
01174 void VehicleEnterDepot(Vehicle *v)
01175 {
01176
01177 assert(v == v->First());
01178
01179 switch (v->type) {
01180 case VEH_TRAIN: {
01181 Train *t = Train::From(v);
01182 SetWindowClassesDirty(WC_TRAINS_LIST);
01183
01184 SetDepotReservation(t->tile, false);
01185 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
01186
01187 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
01188 t->wait_counter = 0;
01189 t->force_proceed = TFP_NONE;
01190 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
01191 t->ConsistChanged(true);
01192 break;
01193 }
01194
01195 case VEH_ROAD:
01196 SetWindowClassesDirty(WC_ROADVEH_LIST);
01197 break;
01198
01199 case VEH_SHIP: {
01200 SetWindowClassesDirty(WC_SHIPS_LIST);
01201 Ship *ship = Ship::From(v);
01202 ship->state = TRACK_BIT_DEPOT;
01203 ship->UpdateCache();
01204 ship->UpdateViewport(true, true);
01205 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01206 break;
01207 }
01208
01209 case VEH_AIRCRAFT:
01210 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01211 HandleAircraftEnterHangar(Aircraft::From(v));
01212 break;
01213 default: NOT_REACHED();
01214 }
01215 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01216
01217 if (v->type != VEH_TRAIN) {
01218
01219
01220 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01221 }
01222 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01223
01224 v->vehstatus |= VS_HIDDEN;
01225 v->cur_speed = 0;
01226
01227 VehicleServiceInDepot(v);
01228
01229 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01230
01231 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01232 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01233
01234 const Order *real_order = v->GetOrder(v->cur_order_index);
01235 Order t = v->current_order;
01236 v->current_order.MakeDummy();
01237
01238
01239
01240 if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
01241 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
01242 (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
01243
01244 return;
01245 }
01246
01247 if (t.IsRefit()) {
01248 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01249 CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
01250 cur_company.Restore();
01251
01252 if (cost.Failed()) {
01253 _vehicles_to_autoreplace[v] = false;
01254 if (v->owner == _local_company) {
01255
01256 SetDParam(0, v->index);
01257 AddVehicleNewsItem(STR_NEWS_ORDER_REFIT_FAILED, NS_ADVICE, v->index);
01258 }
01259 } else if (cost.GetCost() != 0) {
01260 v->profit_this_year -= cost.GetCost() << 8;
01261 if (v->owner == _local_company) {
01262 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01263 }
01264 }
01265 }
01266
01267 if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01268
01269 UpdateVehicleTimetable(v, true);
01270 v->IncrementOrderIndex();
01271 }
01272 if (t.GetDepotActionType() & ODATFB_HALT) {
01273
01274 _vehicles_to_autoreplace[v] = false;
01275 if (v->owner == _local_company) {
01276 SetDParam(0, v->index);
01277 AddVehicleNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, NS_ADVICE, v->index);
01278 }
01279 AI::NewEvent(v->owner, new AIEventVehicleWaitingInDepot(v->index));
01280 }
01281 }
01282 }
01283
01284
01292 void VehicleMove(Vehicle *v, bool update_viewport)
01293 {
01294 int img = v->cur_image;
01295 Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
01296 const Sprite *spr = GetSprite(img, ST_NORMAL);
01297
01298 pt.x += spr->x_offs;
01299 pt.y += spr->y_offs;
01300
01301 UpdateVehiclePosHash(v, pt.x, pt.y);
01302
01303 Rect old_coord = v->coord;
01304 v->coord.left = pt.x;
01305 v->coord.top = pt.y;
01306 v->coord.right = pt.x + spr->width + 2;
01307 v->coord.bottom = pt.y + spr->height + 2;
01308
01309 if (update_viewport) {
01310 MarkAllViewportsDirty(
01311 min(old_coord.left, v->coord.left),
01312 min(old_coord.top, v->coord.top),
01313 max(old_coord.right, v->coord.right) + 1,
01314 max(old_coord.bottom, v->coord.bottom) + 1
01315 );
01316 }
01317 }
01318
01327 void MarkSingleVehicleDirty(const Vehicle *v)
01328 {
01329 MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1, v->coord.bottom + 1);
01330 }
01331
01337 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01338 {
01339 static const int8 _delta_coord[16] = {
01340 -1,-1,-1, 0, 1, 1, 1, 0,
01341 -1, 0, 1, 1, 1, 0,-1,-1,
01342 };
01343
01344 int x = v->x_pos + _delta_coord[v->direction];
01345 int y = v->y_pos + _delta_coord[v->direction + 8];
01346
01347 GetNewVehiclePosResult gp;
01348 gp.x = x;
01349 gp.y = y;
01350 gp.old_tile = v->tile;
01351 gp.new_tile = TileVirtXY(x, y);
01352 return gp;
01353 }
01354
01355 static const Direction _new_direction_table[] = {
01356 DIR_N, DIR_NW, DIR_W,
01357 DIR_NE, DIR_SE, DIR_SW,
01358 DIR_E, DIR_SE, DIR_S
01359 };
01360
01361 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01362 {
01363 int i = 0;
01364
01365 if (y >= v->y_pos) {
01366 if (y != v->y_pos) i += 3;
01367 i += 3;
01368 }
01369
01370 if (x >= v->x_pos) {
01371 if (x != v->x_pos) i++;
01372 i++;
01373 }
01374
01375 Direction dir = v->direction;
01376
01377 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01378 if (dirdiff == DIRDIFF_SAME) return dir;
01379 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01380 }
01381
01391 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01392 {
01393 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01394 }
01395
01396 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01397 {
01398
01399 const Vehicle *v;
01400 FOR_ALL_VEHICLES(v) {
01401 if (v->type == type && v->owner == owner) {
01402 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01403 }
01404 }
01405
01406 if (this->maxid == 0) return;
01407
01408
01409
01410
01411 this->cache = CallocT<bool>(this->maxid + 2);
01412
01413
01414 FOR_ALL_VEHICLES(v) {
01415 if (v->type == type && v->owner == owner) {
01416 this->cache[v->unitnumber] = true;
01417 }
01418 }
01419 }
01420
01421 UnitID FreeUnitIDGenerator::NextID()
01422 {
01423 if (this->maxid <= this->curid) return ++this->curid;
01424
01425 while (this->cache[++this->curid]) { }
01426
01427 return this->curid;
01428 }
01429
01435 UnitID GetFreeUnitNumber(VehicleType type)
01436 {
01437
01438 uint max_veh;
01439 switch (type) {
01440 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
01441 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
01442 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
01443 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
01444 default: NOT_REACHED();
01445 }
01446
01447 uint amounts[4];
01448 CountCompanyVehicles(_current_company, amounts);
01449 assert((uint)type < lengthof(amounts));
01450 if (amounts[type] >= max_veh) return UINT16_MAX;
01451
01452 FreeUnitIDGenerator gen(type, _current_company);
01453
01454 return gen.NextID();
01455 }
01456
01457
01466 bool CanBuildVehicleInfrastructure(VehicleType type)
01467 {
01468 assert(IsCompanyBuildableVehicleType(type));
01469
01470 if (!Company::IsValidID(_local_company)) return false;
01471 if (_settings_client.gui.always_build_infrastructure) return true;
01472
01473 UnitID max;
01474 switch (type) {
01475 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
01476 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
01477 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
01478 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01479 default: NOT_REACHED();
01480 }
01481
01482
01483 if (max > 0) {
01484
01485 const Engine *e;
01486 FOR_ALL_ENGINES_OF_TYPE(e, type) {
01487 if (HasBit(e->company_avail, _local_company)) return true;
01488 }
01489 return false;
01490 }
01491
01492
01493 const Vehicle *v;
01494 FOR_ALL_VEHICLES(v) {
01495 if (v->owner == _local_company && v->type == type) return true;
01496 }
01497
01498 return false;
01499 }
01500
01501
01509 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
01510 {
01511 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01512 const Engine *e = Engine::Get(engine_type);
01513 switch (e->type) {
01514 default: NOT_REACHED();
01515 case VEH_TRAIN:
01516 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (Train::From(v)->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
01517
01518
01519 engine_type = parent_engine_type;
01520 e = Engine::Get(engine_type);
01521
01522 }
01523
01524 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01525 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01526 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
01527 if (!CargoSpec::Get(cargo_type)->is_freight) {
01528 if (parent_engine_type == INVALID_ENGINE) {
01529 return LS_PASSENGER_WAGON_STEAM;
01530 } else {
01531 switch (RailVehInfo(parent_engine_type)->engclass) {
01532 default: NOT_REACHED();
01533 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
01534 case EC_DIESEL: return LS_PASSENGER_WAGON_DIESEL;
01535 case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
01536 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
01537 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
01538 }
01539 }
01540 } else {
01541 return LS_FREIGHT_WAGON;
01542 }
01543 } else {
01544 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
01545
01546 switch (e->u.rail.engclass) {
01547 default: NOT_REACHED();
01548 case EC_STEAM: return LS_STEAM;
01549 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
01550 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
01551 case EC_MONORAIL: return LS_MONORAIL;
01552 case EC_MAGLEV: return LS_MAGLEV;
01553 }
01554 }
01555
01556 case VEH_ROAD:
01557
01558 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01559 engine_type = parent_engine_type;
01560 e = Engine::Get(engine_type);
01561 cargo_type = v->First()->cargo_type;
01562 }
01563 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01564 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01565
01566
01567 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
01568
01569 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01570 } else {
01571
01572 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01573 }
01574
01575 case VEH_SHIP:
01576 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01577 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01578 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01579
01580 case VEH_AIRCRAFT:
01581 switch (e->u.air.subtype) {
01582 case AIR_HELI: return LS_HELICOPTER;
01583 case AIR_CTOL: return LS_SMALL_PLANE;
01584 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
01585 default: NOT_REACHED();
01586 }
01587 }
01588 }
01589
01599 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
01600 {
01601 const Company *c = Company::Get(company);
01602 LiveryScheme scheme = LS_DEFAULT;
01603
01604
01605
01606 if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
01607
01608 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
01609
01610
01611 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01612 }
01613
01614 return &c->livery[scheme];
01615 }
01616
01617
01618 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01619 {
01620 PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01621
01622
01623 if (map != PAL_NONE) return map;
01624
01625 const Engine *e = Engine::Get(engine_type);
01626
01627
01628 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
01629 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01630
01631 if (callback != CALLBACK_FAILED) {
01632 assert_compile(PAL_NONE == 0);
01633 map = GB(callback, 0, 14);
01634
01635
01636 if (!HasBit(callback, 14)) {
01637
01638 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01639 return map;
01640 }
01641 }
01642 }
01643
01644 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
01645
01646 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
01647
01648
01649 if (!Company::IsValidID(company)) return map;
01650
01651 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
01652
01653 map += livery->colour1;
01654 if (twocc) map += livery->colour2 * 16;
01655
01656
01657 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01658 return map;
01659 }
01660
01661 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
01662 {
01663 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01664 }
01665
01666 PaletteID GetVehiclePalette(const Vehicle *v)
01667 {
01668 if (v->IsGroundVehicle()) {
01669 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
01670 }
01671
01672 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01673 }
01674
01683 uint GetVehicleCapacity(const Vehicle *v, uint16 *mail_capacity)
01684 {
01685 if (mail_capacity != NULL) *mail_capacity = 0;
01686 const Engine *e = Engine::Get(v->engine_type);
01687
01688 if (!e->CanCarryCargo()) return 0;
01689
01690 if (mail_capacity != NULL && e->type == VEH_AIRCRAFT && IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
01691 *mail_capacity = GetVehicleProperty(v, PROP_AIRCRAFT_MAIL_CAPACITY, e->u.air.mail_capacity);
01692 }
01693 CargoID default_cargo = e->GetDefaultCargoType();
01694
01695
01696
01697 if (HasBit(e->info.callback_mask, CBM_VEHICLE_REFIT_CAPACITY) &&
01698 (default_cargo != v->cargo_type || v->cargo_subtype != 0)) {
01699 uint16 callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
01700 if (callback != CALLBACK_FAILED) return callback;
01701 }
01702
01703
01704 uint capacity;
01705 switch (e->type) {
01706 case VEH_TRAIN: capacity = GetVehicleProperty(v, PROP_TRAIN_CARGO_CAPACITY, e->u.rail.capacity); break;
01707 case VEH_ROAD: capacity = GetVehicleProperty(v, PROP_ROADVEH_CARGO_CAPACITY, e->u.road.capacity); break;
01708 case VEH_SHIP: capacity = GetVehicleProperty(v, PROP_SHIP_CARGO_CAPACITY, e->u.ship.capacity); break;
01709 case VEH_AIRCRAFT: capacity = GetVehicleProperty(v, PROP_AIRCRAFT_PASSENGER_CAPACITY, e->u.air.passenger_capacity); break;
01710 default: NOT_REACHED();
01711 }
01712
01713
01714
01715 if (e->type != VEH_SHIP) {
01716 if (e->type == VEH_AIRCRAFT) {
01717 if (!IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
01718 capacity += GetVehicleProperty(v, PROP_AIRCRAFT_MAIL_CAPACITY, e->u.air.mail_capacity);
01719 }
01720 if (v->cargo_type == CT_MAIL) return capacity;
01721 } else {
01722 switch (default_cargo) {
01723 case CT_PASSENGERS: break;
01724 case CT_MAIL:
01725 case CT_GOODS: capacity *= 2; break;
01726 default: capacity *= 4; break;
01727 }
01728 }
01729 switch (v->cargo_type) {
01730 case CT_PASSENGERS: break;
01731 case CT_MAIL:
01732 case CT_GOODS: capacity /= 2; break;
01733 default: capacity /= 4; break;
01734 }
01735 }
01736
01737 return capacity;
01738 }
01739
01740
01741 void Vehicle::BeginLoading()
01742 {
01743 assert(IsTileType(tile, MP_STATION) || type == VEH_SHIP);
01744
01745 if (this->current_order.IsType(OT_GOTO_STATION) &&
01746 this->current_order.GetDestination() == this->last_station_visited) {
01747 current_order.MakeLoading(true);
01748 UpdateVehicleTimetable(this, true);
01749
01750
01751
01752
01753
01754
01755 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
01756
01757 } else {
01758 current_order.MakeLoading(false);
01759 }
01760
01761 Station::Get(this->last_station_visited)->loading_vehicles.push_back(this);
01762
01763 PrepareUnload(this);
01764
01765 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
01766 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01767 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01768 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
01769
01770 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
01771 this->cur_speed = 0;
01772 this->MarkDirty();
01773 }
01774
01775 void Vehicle::LeaveStation()
01776 {
01777 assert(current_order.IsType(OT_LOADING));
01778
01779 delete this->cargo_payment;
01780
01781
01782 if (current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
01783
01784 current_order.MakeLeaveStation();
01785 Station *st = Station::Get(this->last_station_visited);
01786 st->loading_vehicles.remove(this);
01787
01788 HideFillingPercent(&this->fill_percent_te_id);
01789
01790 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
01791
01792 if (IsTileType(this->tile, MP_STATION)) TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
01793
01794 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
01795 }
01796 }
01797
01798
01799 void Vehicle::HandleLoading(bool mode)
01800 {
01801 switch (this->current_order.GetType()) {
01802 case OT_LOADING: {
01803 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
01804
01805
01806 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) ||
01807 (_settings_game.order.timetabling && this->current_order_time < wait_time)) return;
01808
01809 this->PlayLeaveStationSound();
01810
01811 bool at_destination_station = this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE;
01812 this->LeaveStation();
01813
01814
01815 if (!at_destination_station) return;
01816 break;
01817 }
01818
01819 case OT_DUMMY: break;
01820
01821 default: return;
01822 }
01823
01824 this->IncrementOrderIndex();
01825 }
01826
01827 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
01828 {
01829 CommandCost ret = CheckOwnership(this->owner);
01830 if (ret.Failed()) return ret;
01831
01832 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
01833 if (this->IsStoppedInDepot()) return CMD_ERROR;
01834
01835 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
01836 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
01837 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
01838
01839
01840
01841 if (flags & DC_EXEC) {
01842 this->current_order.SetDepotOrderType(ODTF_MANUAL);
01843 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
01844 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01845 }
01846 return CommandCost();
01847 }
01848
01849 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR;
01850 if (flags & DC_EXEC) {
01851
01852
01853 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementOrderIndex();
01854
01855 this->current_order.MakeDummy();
01856 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01857 }
01858 return CommandCost();
01859 }
01860
01861 TileIndex location;
01862 DestinationID destination;
01863 bool reverse;
01864 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};
01865 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
01866
01867 if (flags & DC_EXEC) {
01868 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
01869
01870 this->dest_tile = location;
01871 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
01872 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
01873 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01874
01875
01876 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
01877
01878 if (this->type == VEH_AIRCRAFT) {
01879 Aircraft *a = Aircraft::From(this);
01880 if (a->state == FLYING && a->targetairport != destination) {
01881
01882 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
01883 AircraftNextAirportPos_and_Order(a);
01884 }
01885 }
01886 }
01887
01888 return CommandCost();
01889
01890 }
01891
01892 void Vehicle::UpdateVisualEffect(bool allow_power_change)
01893 {
01894 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
01895 const Engine *e = Engine::Get(this->engine_type);
01896
01897
01898 byte visual_effect;
01899 switch (e->type) {
01900 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
01901 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
01902 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
01903 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
01904 }
01905
01906
01907 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
01908 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
01909
01910 if (callback != CALLBACK_FAILED) {
01911 callback = GB(callback, 0, 8);
01912
01913
01914 if (callback == VE_DEFAULT) {
01915 assert(HasBit(callback, VE_DISABLE_EFFECT));
01916 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
01917 }
01918 visual_effect = callback;
01919 }
01920 }
01921
01922
01923 if (visual_effect == VE_DEFAULT ||
01924 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
01925
01926
01927 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
01928 if (visual_effect == VE_DEFAULT) {
01929 visual_effect = 1 << VE_DISABLE_EFFECT;
01930 } else {
01931 SetBit(visual_effect, VE_DISABLE_EFFECT);
01932 }
01933 } else {
01934 if (visual_effect == VE_DEFAULT) {
01935
01936 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
01937 }
01938 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
01939 }
01940 }
01941
01942 this->vcache.cached_vis_effect = visual_effect;
01943
01944 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
01945 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
01946 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
01947 }
01948 }
01949
01950 static const int8 _vehicle_smoke_pos[8] = {
01951 1, 1, 1, 0, -1, -1, -1, 0
01952 };
01953
01954 void Vehicle::ShowVisualEffect() const
01955 {
01956 assert(this->IsPrimaryVehicle());
01957 bool sound = false;
01958
01959
01960
01961
01962
01963
01964 if (_settings_game.vehicle.smoke_amount == 0 ||
01965 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
01966 this->cur_speed < 2) {
01967 return;
01968 }
01969 if (this->type == VEH_TRAIN) {
01970 const Train *t = Train::From(this);
01971
01972
01973
01974
01975 if (HasBit(t->flags, VRF_REVERSING) ||
01976 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
01977 t->cur_speed >= t->Train::GetCurrentMaxSpeed())) {
01978 return;
01979 }
01980 }
01981
01982 const Vehicle *v = this;
01983
01984 do {
01985 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
01986 byte effect_type = GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
01987 bool disable_effect = HasBit(v->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
01988
01989
01990
01991
01992
01993
01994
01995 if (disable_effect ||
01996 v->vehstatus & VS_HIDDEN ||
01997 IsDepotTile(v->tile) ||
01998 IsTunnelTile(v->tile) ||
01999 (v->type == VEH_TRAIN &&
02000 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
02001 continue;
02002 }
02003
02004 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
02005 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
02006
02007 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
02008 x = -x;
02009 y = -y;
02010 }
02011
02012 switch (effect_type) {
02013 case VE_TYPE_STEAM:
02014
02015
02016
02017
02018
02019 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / this->vcache.cached_max_speed))) == 0) {
02020 CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
02021 sound = true;
02022 }
02023 break;
02024
02025 case VE_TYPE_DIESEL: {
02026
02027
02028
02029
02030
02031
02032
02033
02034
02035
02036
02037 int power_weight_effect = 0;
02038 if (v->type == VEH_TRAIN) {
02039 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
02040 }
02041 if (this->cur_speed < (this->vcache.cached_max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
02042 Chance16((64 - ((this->cur_speed << 5) / this->vcache.cached_max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
02043 CreateEffectVehicleRel(v, x, y, 10, EV_DIESEL_SMOKE);
02044 sound = true;
02045 }
02046 break;
02047 }
02048
02049 case VE_TYPE_ELECTRIC:
02050
02051
02052
02053
02054
02055
02056 if (GB(v->tick_counter, 0, 2) == 0 &&
02057 Chance16((6 - ((this->cur_speed << 2) / this->vcache.cached_max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
02058 CreateEffectVehicleRel(v, x, y, 10, EV_ELECTRIC_SPARK);
02059 sound = true;
02060 }
02061 break;
02062
02063 default:
02064 break;
02065 }
02066 } while ((v = v->Next()) != NULL);
02067
02068 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
02069 }
02070
02071 void Vehicle::SetNext(Vehicle *next)
02072 {
02073 assert(this != next);
02074
02075 if (this->next != NULL) {
02076
02077 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02078 v->first = this->next;
02079 }
02080 this->next->previous = NULL;
02081 }
02082
02083 this->next = next;
02084
02085 if (this->next != NULL) {
02086
02087 if (this->next->previous != NULL) this->next->previous->next = NULL;
02088 this->next->previous = this;
02089 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02090 v->first = this->first;
02091 }
02092 }
02093 }
02094
02095 void Vehicle::AddToShared(Vehicle *shared_chain)
02096 {
02097 assert(this->previous_shared == NULL && this->next_shared == NULL);
02098
02099 if (!shared_chain->orders.list) {
02100 assert(shared_chain->previous_shared == NULL);
02101 assert(shared_chain->next_shared == NULL);
02102 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
02103 }
02104
02105 this->next_shared = shared_chain->next_shared;
02106 this->previous_shared = shared_chain;
02107
02108 shared_chain->next_shared = this;
02109
02110 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
02111
02112 shared_chain->orders.list->AddVehicle(this);
02113 }
02114
02115 void Vehicle::RemoveFromShared()
02116 {
02117
02118
02119 bool were_first = (this->FirstShared() == this);
02120 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
02121
02122 this->orders.list->RemoveVehicle(this);
02123
02124 if (!were_first) {
02125
02126 this->previous_shared->next_shared = this->NextShared();
02127 }
02128
02129 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
02130
02131
02132 if (this->orders.list->GetNumVehicles() == 1) {
02133
02134 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
02135 InvalidateVehicleOrder(this->FirstShared(), 0);
02136 } else if (were_first) {
02137
02138
02139 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
02140 }
02141
02142 this->next_shared = NULL;
02143 this->previous_shared = NULL;
02144 }
02145
02146 void StopAllVehicles()
02147 {
02148 Vehicle *v;
02149 FOR_ALL_VEHICLES(v) {
02150
02151
02152 v->vehstatus |= VS_STOPPED;
02153 v->MarkDirty();
02154 SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
02155 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
02156 }
02157 }
02158
02159 void VehiclesYearlyLoop()
02160 {
02161 Vehicle *v;
02162 FOR_ALL_VEHICLES(v) {
02163 if (v->IsPrimaryVehicle()) {
02164
02165 Money profit = v->GetDisplayProfitThisYear();
02166 if (v->age >= 730 && profit < 0) {
02167 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
02168 SetDParam(0, v->index);
02169 SetDParam(1, profit);
02170 AddVehicleNewsItem(
02171 STR_NEWS_VEHICLE_IS_UNPROFITABLE,
02172 NS_ADVICE,
02173 v->index
02174 );
02175 }
02176 AI::NewEvent(v->owner, new AIEventVehicleUnprofitable(v->index));
02177 }
02178
02179 v->profit_last_year = v->profit_this_year;
02180 v->profit_this_year = 0;
02181 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
02182 }
02183 }
02184 }
02185
02186
02196 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
02197 {
02198 const Engine *e = Engine::GetIfValid(engine_type);
02199 assert(e != NULL);
02200
02201 switch (e->type) {
02202 case VEH_TRAIN:
02203 return (st->facilities & FACIL_TRAIN) != 0;
02204
02205 case VEH_ROAD:
02206
02207
02208
02209 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
02210
02211 case VEH_SHIP:
02212 return (st->facilities & FACIL_DOCK) != 0;
02213
02214 case VEH_AIRCRAFT:
02215 return (st->facilities & FACIL_AIRPORT) != 0 &&
02216 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
02217
02218 default:
02219 return false;
02220 }
02221 }
02222
02229 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
02230 {
02231 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
02232
02233 return CanVehicleUseStation(v->engine_type, st);
02234 }
02235
02241 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
02242 {
02243 assert(this->IsGroundVehicle());
02244 if (this->type == VEH_TRAIN) {
02245 return &Train::From(this)->gcache;
02246 } else {
02247 return &RoadVehicle::From(this)->gcache;
02248 }
02249 }
02250
02256 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
02257 {
02258 assert(this->IsGroundVehicle());
02259 if (this->type == VEH_TRAIN) {
02260 return &Train::From(this)->gcache;
02261 } else {
02262 return &RoadVehicle::From(this)->gcache;
02263 }
02264 }
02265
02274 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
02275 {
02276 if (v->type == VEH_TRAIN) {
02277 Train *u = Train::From(v);
02278
02279 if (u->IsArticulatedPart()) {
02280 u = u->GetFirstEnginePart();
02281 while (u->index != v->index) {
02282 set.Include(u->index);
02283 u = u->GetNextArticPart();
02284 }
02285 }
02286
02287 for (;u != NULL && num_vehicles > 0; num_vehicles--, u = u->Next()) {
02288
02289 set.Include(u->index);
02290
02291
02292 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
02293 }
02294
02295
02296 while (u != NULL && u->IsArticulatedPart()) {
02297 set.Include(u->index);
02298 u = u->Next();
02299 }
02300 }
02301 }