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->GetNextManualOrder(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 const Order *order = this->GetOrder(this->cur_order_index);
01751 while (order != NULL && order->IsType(OT_AUTOMATIC)) {
01752
01753 order = order->next;
01754 DeleteOrder(this, this->cur_order_index);
01755 }
01756
01757
01758
01759
01760
01761
01762 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
01763
01764 } else {
01765
01766
01767 Order *in_list = this->GetOrder(this->cur_order_index);
01768 if ((this->orders.list == NULL || this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID) &&
01769 ((in_list == NULL && this->cur_order_index == 0) ||
01770 (in_list != NULL && (!in_list->IsType(OT_AUTOMATIC) ||
01771 in_list->GetDestination() != this->last_station_visited)))) {
01772 Order *auto_order = new Order();
01773 auto_order->MakeAutomatic(this->last_station_visited);
01774 InsertOrder(this, auto_order, this->cur_order_index);
01775 if (this->cur_order_index > 0) --this->cur_order_index;
01776 }
01777 current_order.MakeLoading(false);
01778 }
01779
01780 Station::Get(this->last_station_visited)->loading_vehicles.push_back(this);
01781
01782 PrepareUnload(this);
01783
01784 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
01785 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01786 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01787 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
01788
01789 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
01790 this->cur_speed = 0;
01791 this->MarkDirty();
01792 }
01793
01794 void Vehicle::LeaveStation()
01795 {
01796 assert(current_order.IsType(OT_LOADING));
01797
01798 delete this->cargo_payment;
01799
01800
01801 if (current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
01802
01803 current_order.MakeLeaveStation();
01804 Station *st = Station::Get(this->last_station_visited);
01805 st->loading_vehicles.remove(this);
01806
01807 HideFillingPercent(&this->fill_percent_te_id);
01808
01809 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
01810
01811 if (IsTileType(this->tile, MP_STATION)) TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
01812
01813 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
01814 }
01815 }
01816
01817
01818 void Vehicle::HandleLoading(bool mode)
01819 {
01820 switch (this->current_order.GetType()) {
01821 case OT_LOADING: {
01822 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
01823
01824
01825 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) ||
01826 (_settings_game.order.timetabling && this->current_order_time < wait_time)) return;
01827
01828 this->PlayLeaveStationSound();
01829
01830 this->LeaveStation();
01831
01832 break;
01833 }
01834
01835 case OT_DUMMY: break;
01836
01837 default: return;
01838 }
01839
01840 this->IncrementOrderIndex();
01841 }
01842
01843 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
01844 {
01845 CommandCost ret = CheckOwnership(this->owner);
01846 if (ret.Failed()) return ret;
01847
01848 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
01849 if (this->IsStoppedInDepot()) return CMD_ERROR;
01850
01851 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
01852 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
01853 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
01854
01855
01856
01857 if (flags & DC_EXEC) {
01858 this->current_order.SetDepotOrderType(ODTF_MANUAL);
01859 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
01860 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01861 }
01862 return CommandCost();
01863 }
01864
01865 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR;
01866 if (flags & DC_EXEC) {
01867
01868
01869 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementOrderIndex();
01870
01871 this->current_order.MakeDummy();
01872 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01873 }
01874 return CommandCost();
01875 }
01876
01877 TileIndex location;
01878 DestinationID destination;
01879 bool reverse;
01880 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};
01881 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
01882
01883 if (flags & DC_EXEC) {
01884 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
01885
01886 this->dest_tile = location;
01887 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
01888 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
01889 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01890
01891
01892 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
01893
01894 if (this->type == VEH_AIRCRAFT) {
01895 Aircraft *a = Aircraft::From(this);
01896 if (a->state == FLYING && a->targetairport != destination) {
01897
01898 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
01899 AircraftNextAirportPos_and_Order(a);
01900 }
01901 }
01902 }
01903
01904 return CommandCost();
01905
01906 }
01907
01908 void Vehicle::UpdateVisualEffect(bool allow_power_change)
01909 {
01910 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
01911 const Engine *e = Engine::Get(this->engine_type);
01912
01913
01914 byte visual_effect;
01915 switch (e->type) {
01916 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
01917 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
01918 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
01919 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
01920 }
01921
01922
01923 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
01924 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
01925
01926 if (callback != CALLBACK_FAILED) {
01927 callback = GB(callback, 0, 8);
01928
01929
01930 if (callback == VE_DEFAULT) {
01931 assert(HasBit(callback, VE_DISABLE_EFFECT));
01932 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
01933 }
01934 visual_effect = callback;
01935 }
01936 }
01937
01938
01939 if (visual_effect == VE_DEFAULT ||
01940 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
01941
01942
01943 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
01944 if (visual_effect == VE_DEFAULT) {
01945 visual_effect = 1 << VE_DISABLE_EFFECT;
01946 } else {
01947 SetBit(visual_effect, VE_DISABLE_EFFECT);
01948 }
01949 } else {
01950 if (visual_effect == VE_DEFAULT) {
01951
01952 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
01953 }
01954 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
01955 }
01956 }
01957
01958 this->vcache.cached_vis_effect = visual_effect;
01959
01960 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
01961 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
01962 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
01963 }
01964 }
01965
01966 static const int8 _vehicle_smoke_pos[8] = {
01967 1, 1, 1, 0, -1, -1, -1, 0
01968 };
01969
01970 void Vehicle::ShowVisualEffect() const
01971 {
01972 assert(this->IsPrimaryVehicle());
01973 bool sound = false;
01974
01975
01976
01977
01978
01979
01980 if (_settings_game.vehicle.smoke_amount == 0 ||
01981 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
01982 this->cur_speed < 2) {
01983 return;
01984 }
01985 if (this->type == VEH_TRAIN) {
01986 const Train *t = Train::From(this);
01987
01988
01989
01990
01991 if (HasBit(t->flags, VRF_REVERSING) ||
01992 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
01993 t->cur_speed >= t->Train::GetCurrentMaxSpeed())) {
01994 return;
01995 }
01996 }
01997
01998 const Vehicle *v = this;
01999
02000 do {
02001 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
02002 byte effect_type = GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
02003 bool disable_effect = HasBit(v->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
02004
02005
02006
02007
02008
02009
02010
02011 if (disable_effect ||
02012 v->vehstatus & VS_HIDDEN ||
02013 IsDepotTile(v->tile) ||
02014 IsTunnelTile(v->tile) ||
02015 (v->type == VEH_TRAIN &&
02016 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
02017 continue;
02018 }
02019
02020 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
02021 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
02022
02023 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
02024 x = -x;
02025 y = -y;
02026 }
02027
02028 switch (effect_type) {
02029 case VE_TYPE_STEAM:
02030
02031
02032
02033
02034
02035 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / this->vcache.cached_max_speed))) == 0) {
02036 CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
02037 sound = true;
02038 }
02039 break;
02040
02041 case VE_TYPE_DIESEL: {
02042
02043
02044
02045
02046
02047
02048
02049
02050
02051
02052
02053 int power_weight_effect = 0;
02054 if (v->type == VEH_TRAIN) {
02055 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
02056 }
02057 if (this->cur_speed < (this->vcache.cached_max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
02058 Chance16((64 - ((this->cur_speed << 5) / this->vcache.cached_max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
02059 CreateEffectVehicleRel(v, x, y, 10, EV_DIESEL_SMOKE);
02060 sound = true;
02061 }
02062 break;
02063 }
02064
02065 case VE_TYPE_ELECTRIC:
02066
02067
02068
02069
02070
02071
02072 if (GB(v->tick_counter, 0, 2) == 0 &&
02073 Chance16((6 - ((this->cur_speed << 2) / this->vcache.cached_max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
02074 CreateEffectVehicleRel(v, x, y, 10, EV_ELECTRIC_SPARK);
02075 sound = true;
02076 }
02077 break;
02078
02079 default:
02080 break;
02081 }
02082 } while ((v = v->Next()) != NULL);
02083
02084 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
02085 }
02086
02087 void Vehicle::SetNext(Vehicle *next)
02088 {
02089 assert(this != next);
02090
02091 if (this->next != NULL) {
02092
02093 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02094 v->first = this->next;
02095 }
02096 this->next->previous = NULL;
02097 }
02098
02099 this->next = next;
02100
02101 if (this->next != NULL) {
02102
02103 if (this->next->previous != NULL) this->next->previous->next = NULL;
02104 this->next->previous = this;
02105 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02106 v->first = this->first;
02107 }
02108 }
02109 }
02110
02111 void Vehicle::AddToShared(Vehicle *shared_chain)
02112 {
02113 assert(this->previous_shared == NULL && this->next_shared == NULL);
02114
02115 if (!shared_chain->orders.list) {
02116 assert(shared_chain->previous_shared == NULL);
02117 assert(shared_chain->next_shared == NULL);
02118 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
02119 }
02120
02121 this->next_shared = shared_chain->next_shared;
02122 this->previous_shared = shared_chain;
02123
02124 shared_chain->next_shared = this;
02125
02126 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
02127
02128 shared_chain->orders.list->AddVehicle(this);
02129 }
02130
02131 void Vehicle::RemoveFromShared()
02132 {
02133
02134
02135 bool were_first = (this->FirstShared() == this);
02136 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
02137
02138 this->orders.list->RemoveVehicle(this);
02139
02140 if (!were_first) {
02141
02142 this->previous_shared->next_shared = this->NextShared();
02143 }
02144
02145 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
02146
02147
02148 if (this->orders.list->GetNumVehicles() == 1) {
02149
02150 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
02151 InvalidateVehicleOrder(this->FirstShared(), 0);
02152 } else if (were_first) {
02153
02154
02155 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
02156 }
02157
02158 this->next_shared = NULL;
02159 this->previous_shared = NULL;
02160 }
02161
02167 Order *Vehicle::GetNextManualOrder(int index) const
02168 {
02169 Order *order = this->GetOrder(index);
02170 while(order != NULL && order->IsType(OT_AUTOMATIC)) {
02171 order = order->next;
02172 }
02173 return order;
02174 }
02175
02176 void StopAllVehicles()
02177 {
02178 Vehicle *v;
02179 FOR_ALL_VEHICLES(v) {
02180
02181
02182 v->vehstatus |= VS_STOPPED;
02183 v->MarkDirty();
02184 SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
02185 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
02186 }
02187 }
02188
02189 void VehiclesYearlyLoop()
02190 {
02191 Vehicle *v;
02192 FOR_ALL_VEHICLES(v) {
02193 if (v->IsPrimaryVehicle()) {
02194
02195 Money profit = v->GetDisplayProfitThisYear();
02196 if (v->age >= 730 && profit < 0) {
02197 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
02198 SetDParam(0, v->index);
02199 SetDParam(1, profit);
02200 AddVehicleNewsItem(
02201 STR_NEWS_VEHICLE_IS_UNPROFITABLE,
02202 NS_ADVICE,
02203 v->index
02204 );
02205 }
02206 AI::NewEvent(v->owner, new AIEventVehicleUnprofitable(v->index));
02207 }
02208
02209 v->profit_last_year = v->profit_this_year;
02210 v->profit_this_year = 0;
02211 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
02212 }
02213 }
02214 }
02215
02216
02226 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
02227 {
02228 const Engine *e = Engine::GetIfValid(engine_type);
02229 assert(e != NULL);
02230
02231 switch (e->type) {
02232 case VEH_TRAIN:
02233 return (st->facilities & FACIL_TRAIN) != 0;
02234
02235 case VEH_ROAD:
02236
02237
02238
02239 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
02240
02241 case VEH_SHIP:
02242 return (st->facilities & FACIL_DOCK) != 0;
02243
02244 case VEH_AIRCRAFT:
02245 return (st->facilities & FACIL_AIRPORT) != 0 &&
02246 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
02247
02248 default:
02249 return false;
02250 }
02251 }
02252
02259 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
02260 {
02261 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
02262
02263 return CanVehicleUseStation(v->engine_type, st);
02264 }
02265
02271 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
02272 {
02273 assert(this->IsGroundVehicle());
02274 if (this->type == VEH_TRAIN) {
02275 return &Train::From(this)->gcache;
02276 } else {
02277 return &RoadVehicle::From(this)->gcache;
02278 }
02279 }
02280
02286 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
02287 {
02288 assert(this->IsGroundVehicle());
02289 if (this->type == VEH_TRAIN) {
02290 return &Train::From(this)->gcache;
02291 } else {
02292 return &RoadVehicle::From(this)->gcache;
02293 }
02294 }
02295
02304 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
02305 {
02306 if (v->type == VEH_TRAIN) {
02307 Train *u = Train::From(v);
02308
02309 if (u->IsArticulatedPart()) {
02310 u = u->GetFirstEnginePart();
02311 while (u->index != v->index) {
02312 set.Include(u->index);
02313 u = u->GetNextArticPart();
02314 }
02315 }
02316
02317 for (;u != NULL && num_vehicles > 0; num_vehicles--, u = u->Next()) {
02318
02319 set.Include(u->index);
02320
02321
02322 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
02323 }
02324
02325
02326 while (u != NULL && u->IsArticulatedPart()) {
02327 set.Include(u->index);
02328 u = u->Next();
02329 }
02330 }
02331 }