00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include "error.h"
00014 #include "roadveh.h"
00015 #include "ship.h"
00016 #include "spritecache.h"
00017 #include "timetable.h"
00018 #include "viewport_func.h"
00019 #include "news_func.h"
00020 #include "command_func.h"
00021 #include "company_func.h"
00022 #include "vehicle_gui.h"
00023 #include "train.h"
00024 #include "aircraft.h"
00025 #include "newgrf_debug.h"
00026 #include "newgrf_sound.h"
00027 #include "newgrf_station.h"
00028 #include "group.h"
00029 #include "group_gui.h"
00030 #include "strings_func.h"
00031 #include "zoom_func.h"
00032 #include "date_func.h"
00033 #include "window_func.h"
00034 #include "vehicle_func.h"
00035 #include "autoreplace_func.h"
00036 #include "autoreplace_gui.h"
00037 #include "station_base.h"
00038 #include "ai/ai.hpp"
00039 #include "depot_func.h"
00040 #include "network/network.h"
00041 #include "core/pool_func.hpp"
00042 #include "economy_base.h"
00043 #include "articulated_vehicles.h"
00044 #include "roadstop_base.h"
00045 #include "core/random_func.hpp"
00046 #include "core/backup_type.hpp"
00047 #include "order_backup.h"
00048 #include "sound_func.h"
00049 #include "effectvehicle_func.h"
00050 #include "effectvehicle_base.h"
00051 #include "vehiclelist.h"
00052 #include "bridge_map.h"
00053 #include "tunnel_map.h"
00054 #include "depot_map.h"
00055 #include "gamelog.h"
00056
00057 #include "table/strings.h"
00058
00059 #define GEN_HASH(x, y) ((GB((y), 6 + ZOOM_LVL_SHIFT, 6) << 6) + GB((x), 7 + ZOOM_LVL_SHIFT, 6))
00060
00061 VehicleID _new_vehicle_id;
00062 uint16 _returned_refit_capacity;
00063 uint16 _returned_mail_refit_capacity;
00064
00065
00067 VehiclePool _vehicle_pool("Vehicle");
00068 INSTANTIATE_POOL_METHODS(Vehicle)
00069
00070
00075 bool Vehicle::NeedsAutorenewing(const Company *c) const
00076 {
00077
00078
00079
00080
00081 assert(c == Company::Get(this->owner));
00082
00083 if (!c->settings.engine_renew) return false;
00084 if (this->age - this->max_age < (c->settings.engine_renew_months * 30)) return false;
00085
00086
00087 if (this->type == VEH_TRAIN && !Train::From(this)->IsEngine()) 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 = v->GetEngine()->reliability;
00097 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
00098 }
00099
00106 bool Vehicle::NeedsServicing() const
00107 {
00108
00109
00110 if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
00111
00112
00113 const Company *c = Company::Get(this->owner);
00114 if (c->settings.vehicle.servint_ispercent ?
00115 (this->reliability >= this->GetEngine()->reliability * (100 - this->service_interval) / 100) :
00116 (this->date_of_last_service + this->service_interval >= _date)) {
00117 return false;
00118 }
00119
00120
00121
00122 if (!_settings_game.order.no_servicing_if_no_breakdowns ||
00123 _settings_game.difficulty.vehicle_breakdowns != 0) {
00124 return true;
00125 }
00126
00127
00128
00129
00130 bool pending_replace = false;
00131 Money needed_money = c->settings.engine_renew_money;
00132 if (needed_money > c->money) return false;
00133
00134 for (const Vehicle *v = this; v != NULL; v = (v->type == VEH_TRAIN) ? Train::From(v)->GetNextUnit() : NULL) {
00135 EngineID new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id);
00136
00137
00138 if (new_engine == INVALID_ENGINE || !HasBit(Engine::Get(new_engine)->company_avail, v->owner)) continue;
00139
00140
00141 uint32 available_cargo_types, union_mask;
00142 GetArticulatedRefitMasks(new_engine, true, &union_mask, &available_cargo_types);
00143
00144 if (union_mask != 0) {
00145 CargoID cargo_type;
00146
00147 if (IsArticulatedVehicleCarryingDifferentCargoes(v, &cargo_type)) continue;
00148
00149
00150 if (cargo_type != CT_INVALID) {
00151
00152 if (!HasBit(available_cargo_types, cargo_type)) continue;
00153 }
00154 }
00155
00156
00157
00158 pending_replace = true;
00159 needed_money += 2 * Engine::Get(new_engine)->GetCost();
00160 if (needed_money > c->money) return false;
00161 }
00162
00163 return pending_replace;
00164 }
00165
00171 bool Vehicle::NeedsAutomaticServicing() const
00172 {
00173 if (this->HasDepotOrder()) return false;
00174 if (this->current_order.IsType(OT_LOADING)) return false;
00175 if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
00176 return NeedsServicing();
00177 }
00178
00179 uint Vehicle::Crash(bool flooded)
00180 {
00181 assert((this->vehstatus & VS_CRASHED) == 0);
00182 assert(this->Previous() == NULL);
00183
00184 uint pass = 0;
00185
00186 if (this->IsPrimaryVehicle()) this->vehstatus |= VS_STOPPED;
00187
00188 for (Vehicle *v = this; v != NULL; v = v->Next()) {
00189 if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo.Count();
00190 v->vehstatus |= VS_CRASHED;
00191 MarkSingleVehicleDirty(v);
00192 }
00193
00194
00195 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00196 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
00197 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
00198 SetWindowDirty(WC_VEHICLE_DEPOT, this->tile);
00199
00200 return pass;
00201 }
00202
00203
00212 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
00213 {
00214 const Engine *e = Engine::Get(engine);
00215 GRFConfig *grfconfig = GetGRFConfig(e->GetGRFID());
00216
00217 if (!HasBit(grfconfig->grf_bugs, bug_type)) {
00218 SetBit(grfconfig->grf_bugs, bug_type);
00219 SetDParamStr(0, grfconfig->GetName());
00220 SetDParam(1, engine);
00221 ShowErrorMessage(part1, part2, WL_CRITICAL);
00222 if (!_networking) DoCommand(0, critical ? PM_PAUSED_ERROR : PM_PAUSED_NORMAL, 1, DC_EXEC, CMD_PAUSE);
00223 }
00224
00225
00226 char buffer[512];
00227
00228 SetDParamStr(0, grfconfig->GetName());
00229 GetString(buffer, part1, lastof(buffer));
00230 DEBUG(grf, 0, "%s", buffer + 3);
00231
00232 SetDParam(1, engine);
00233 GetString(buffer, part2, lastof(buffer));
00234 DEBUG(grf, 0, "%s", buffer + 3);
00235 }
00236
00242 void VehicleLengthChanged(const Vehicle *u)
00243 {
00244
00245 const Engine *engine = u->GetEngine();
00246 uint32 grfid = engine->grf_prop.grffile->grfid;
00247 GRFConfig *grfconfig = GetGRFConfig(grfid);
00248 if (GamelogGRFBugReverse(grfid, engine->grf_prop.local_id) || !HasBit(grfconfig->grf_bugs, GBUG_VEH_LENGTH)) {
00249 ShowNewGrfVehicleError(u->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_VEHICLE_LENGTH, GBUG_VEH_LENGTH, true);
00250 }
00251 }
00252
00257 Vehicle::Vehicle(VehicleType type)
00258 {
00259 this->type = type;
00260 this->coord.left = INVALID_COORD;
00261 this->group_id = DEFAULT_GROUP;
00262 this->fill_percent_te_id = INVALID_TE_ID;
00263 this->first = this;
00264 this->colourmap = PAL_NONE;
00265 this->cargo_age_counter = 1;
00266 }
00267
00272 byte VehicleRandomBits()
00273 {
00274 return GB(Random(), 0, 8);
00275 }
00276
00277
00278
00279 const int HASH_BITS = 7;
00280 const int HASH_SIZE = 1 << HASH_BITS;
00281 const int HASH_MASK = HASH_SIZE - 1;
00282 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
00283 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
00284
00285
00286
00287 const int HASH_RES = 0;
00288
00289 static Vehicle *_vehicle_tile_hash[TOTAL_HASH_SIZE];
00290
00291 static Vehicle *VehicleFromTileHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
00292 {
00293 for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
00294 for (int x = xl; ; x = (x + 1) & HASH_MASK) {
00295 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
00296 for (; v != NULL; v = v->hash_tile_next) {
00297 Vehicle *a = proc(v, data);
00298 if (find_first && a != NULL) return a;
00299 }
00300 if (x == xu) break;
00301 }
00302 if (y == yu) break;
00303 }
00304
00305 return NULL;
00306 }
00307
00308
00320 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
00321 {
00322 const int COLL_DIST = 6;
00323
00324
00325 int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00326 int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00327 int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00328 int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00329
00330 return VehicleFromTileHash(xl, yl, xu, yu, data, proc, find_first);
00331 }
00332
00347 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00348 {
00349 VehicleFromPosXY(x, y, data, proc, false);
00350 }
00351
00363 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00364 {
00365 return VehicleFromPosXY(x, y, data, proc, true) != NULL;
00366 }
00367
00378 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
00379 {
00380 int x = GB(TileX(tile), HASH_RES, HASH_BITS);
00381 int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
00382
00383 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
00384 for (; v != NULL; v = v->hash_tile_next) {
00385 if (v->tile != tile) continue;
00386
00387 Vehicle *a = proc(v, data);
00388 if (find_first && a != NULL) return a;
00389 }
00390
00391 return NULL;
00392 }
00393
00407 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00408 {
00409 VehicleFromPos(tile, data, proc, false);
00410 }
00411
00422 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00423 {
00424 return VehicleFromPos(tile, data, proc, true) != NULL;
00425 }
00426
00433 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
00434 {
00435 int z = *(int*)data;
00436
00437 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
00438 if (v->z_pos > z) return NULL;
00439
00440 return v;
00441 }
00442
00448 CommandCost EnsureNoVehicleOnGround(TileIndex tile)
00449 {
00450 int z = GetTileMaxPixelZ(tile);
00451
00452
00453
00454
00455
00456 Vehicle *v = VehicleFromPos(tile, &z, &EnsureNoVehicleProcZ, true);
00457 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00458 return CommandCost();
00459 }
00460
00462 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
00463 {
00464 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
00465 if (v == (const Vehicle *)data) return NULL;
00466
00467 return v;
00468 }
00469
00477 CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
00478 {
00479
00480
00481
00482
00483 Vehicle *v = VehicleFromPos(tile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
00484 if (v == NULL) v = VehicleFromPos(endtile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
00485
00486 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00487 return CommandCost();
00488 }
00489
00490 static Vehicle *EnsureNoTrainOnTrackProc(Vehicle *v, void *data)
00491 {
00492 TrackBits rail_bits = *(TrackBits *)data;
00493
00494 if (v->type != VEH_TRAIN) return NULL;
00495
00496 Train *t = Train::From(v);
00497 if ((t->track != rail_bits) && !TracksOverlap(t->track | rail_bits)) return NULL;
00498
00499 return v;
00500 }
00501
00510 CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
00511 {
00512
00513
00514
00515
00516 Vehicle *v = VehicleFromPos(tile, &track_bits, &EnsureNoTrainOnTrackProc, true);
00517 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00518 return CommandCost();
00519 }
00520
00521 static void UpdateVehicleTileHash(Vehicle *v, bool remove)
00522 {
00523 Vehicle **old_hash = v->hash_tile_current;
00524 Vehicle **new_hash;
00525
00526 if (remove) {
00527 new_hash = NULL;
00528 } else {
00529 int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
00530 int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
00531 new_hash = &_vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
00532 }
00533
00534 if (old_hash == new_hash) return;
00535
00536
00537 if (old_hash != NULL) {
00538 if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = v->hash_tile_prev;
00539 *v->hash_tile_prev = v->hash_tile_next;
00540 }
00541
00542
00543 if (new_hash != NULL) {
00544 v->hash_tile_next = *new_hash;
00545 if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = &v->hash_tile_next;
00546 v->hash_tile_prev = new_hash;
00547 *new_hash = v;
00548 }
00549
00550
00551 v->hash_tile_current = new_hash;
00552 }
00553
00554 static Vehicle *_vehicle_viewport_hash[0x1000];
00555
00556 static void UpdateVehicleViewportHash(Vehicle *v, int x, int y)
00557 {
00558 Vehicle **old_hash, **new_hash;
00559 int old_x = v->coord.left;
00560 int old_y = v->coord.top;
00561
00562 new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(x, y)];
00563 old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(old_x, old_y)];
00564
00565 if (old_hash == new_hash) return;
00566
00567
00568 if (old_hash != NULL) {
00569 if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = v->hash_viewport_prev;
00570 *v->hash_viewport_prev = v->hash_viewport_next;
00571 }
00572
00573
00574 if (new_hash != NULL) {
00575 v->hash_viewport_next = *new_hash;
00576 if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = &v->hash_viewport_next;
00577 v->hash_viewport_prev = new_hash;
00578 *new_hash = v;
00579 }
00580 }
00581
00582 void ResetVehicleHash()
00583 {
00584 Vehicle *v;
00585 FOR_ALL_VEHICLES(v) { v->hash_tile_current = NULL; }
00586 memset(_vehicle_viewport_hash, 0, sizeof(_vehicle_viewport_hash));
00587 memset(_vehicle_tile_hash, 0, sizeof(_vehicle_tile_hash));
00588 }
00589
00590 void ResetVehicleColourMap()
00591 {
00592 Vehicle *v;
00593 FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
00594 }
00595
00600 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
00601 static AutoreplaceMap _vehicles_to_autoreplace;
00602
00603 void InitializeVehicles()
00604 {
00605 _vehicles_to_autoreplace.Reset();
00606 ResetVehicleHash();
00607 }
00608
00609 uint CountVehiclesInChain(const Vehicle *v)
00610 {
00611 uint count = 0;
00612 do count++; while ((v = v->Next()) != NULL);
00613 return count;
00614 }
00615
00620 bool Vehicle::IsEngineCountable() const
00621 {
00622 switch (this->type) {
00623 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
00624 case VEH_TRAIN:
00625 return !this->IsArticulatedPart() &&
00626 !Train::From(this)->IsRearDualheaded();
00627 case VEH_ROAD: return RoadVehicle::From(this)->IsFrontEngine();
00628 case VEH_SHIP: return true;
00629 default: return false;
00630 }
00631 }
00632
00637 bool Vehicle::HasEngineType() const
00638 {
00639 switch (this->type) {
00640 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
00641 case VEH_TRAIN:
00642 case VEH_ROAD:
00643 case VEH_SHIP: return true;
00644 default: return false;
00645 }
00646 }
00647
00653 const Engine *Vehicle::GetEngine() const
00654 {
00655 return Engine::Get(this->engine_type);
00656 }
00657
00663 const GRFFile *Vehicle::GetGRF() const
00664 {
00665 return this->GetEngine()->GetGRF();
00666 }
00667
00673 uint32 Vehicle::GetGRFID() const
00674 {
00675 return this->GetEngine()->GetGRFID();
00676 }
00677
00685 void Vehicle::HandlePathfindingResult(bool path_found)
00686 {
00687 if (path_found) {
00688
00689 if (!HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00690
00691
00692 ClrBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00693
00694 DeleteVehicleNews(this->index, STR_NEWS_VEHICLE_IS_LOST);
00695 return;
00696 }
00697
00698
00699 if (HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00700
00701
00702 SetBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00703
00704 AI::NewEvent(this->owner, new ScriptEventVehicleLost(this->index));
00705 if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) {
00706 SetDParam(0, this->index);
00707 AddVehicleNewsItem(STR_NEWS_VEHICLE_IS_LOST, NS_ADVICE, this->index);
00708 }
00709 }
00710
00712 void Vehicle::PreDestructor()
00713 {
00714 if (CleaningPool()) return;
00715
00716 if (Station::IsValidID(this->last_station_visited)) {
00717 Station *st = Station::Get(this->last_station_visited);
00718 st->loading_vehicles.remove(this);
00719
00720 HideFillingPercent(&this->fill_percent_te_id);
00721 this->CancelReservation(INVALID_STATION, st);
00722 delete this->cargo_payment;
00723 }
00724
00725 if (this->IsEngineCountable()) {
00726 GroupStatistics::CountEngine(this, -1);
00727 if (this->IsPrimaryVehicle()) GroupStatistics::CountVehicle(this, -1);
00728 GroupStatistics::UpdateAutoreplace(this->owner);
00729
00730 if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
00731 DeleteGroupHighlightOfVehicle(this);
00732 }
00733
00734 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
00735 Aircraft *a = Aircraft::From(this);
00736 Station *st = GetTargetAirportIfValid(a);
00737 if (st != NULL) {
00738 const AirportFTA *layout = st->airport.GetFTA()->layout;
00739 CLRBITS(st->airport.flags, layout[a->previous_pos].block | layout[a->pos].block);
00740 }
00741 }
00742
00743
00744 if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
00745 RoadVehicle *v = RoadVehicle::From(this);
00746 if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
00747
00748 RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
00749 }
00750 }
00751
00752 if (this->Previous() == NULL) {
00753 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
00754 }
00755
00756 if (this->IsPrimaryVehicle()) {
00757 DeleteWindowById(WC_VEHICLE_VIEW, this->index);
00758 DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
00759 DeleteWindowById(WC_VEHICLE_REFIT, this->index);
00760 DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
00761 DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
00762 SetWindowDirty(WC_COMPANY, this->owner);
00763 OrderBackup::ClearVehicle(this);
00764 }
00765 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00766
00767 this->cargo.Truncate(0);
00768 DeleteVehicleOrders(this);
00769 DeleteDepotHighlightOfVehicle(this);
00770
00771 extern void StopGlobalFollowVehicle(const Vehicle *v);
00772 StopGlobalFollowVehicle(this);
00773
00774 ReleaseDisastersTargetingVehicle(this->index);
00775 }
00776
00777 Vehicle::~Vehicle()
00778 {
00779 free(this->name);
00780
00781 if (CleaningPool()) {
00782 this->cargo.OnCleanPool();
00783 return;
00784 }
00785
00786
00787
00788 if (!(this->vehstatus & VS_HIDDEN)) MarkSingleVehicleDirty(this);
00789
00790 Vehicle *v = this->Next();
00791 this->SetNext(NULL);
00792
00793 delete v;
00794
00795 UpdateVehicleTileHash(this, true);
00796 UpdateVehicleViewportHash(this, INVALID_COORD, 0);
00797 DeleteVehicleNews(this->index, INVALID_STRING_ID);
00798 DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index);
00799 }
00800
00805 void VehicleEnteredDepotThisTick(Vehicle *v)
00806 {
00807
00808 _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
00809
00810
00811
00812
00813
00814
00815 v->vehstatus |= VS_STOPPED;
00816 }
00817
00823 static void RunVehicleDayProc()
00824 {
00825 if (_game_mode != GM_NORMAL) return;
00826
00827
00828 for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
00829 Vehicle *v = Vehicle::Get(i);
00830 if (v == NULL) continue;
00831
00832
00833 if ((v->day_counter & 0x1F) == 0 && v->HasEngineType()) {
00834 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
00835 if (callback != CALLBACK_FAILED) {
00836 if (HasBit(callback, 0)) TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32);
00837 if (HasBit(callback, 1)) v->colourmap = PAL_NONE;
00838
00839 if (callback & ~3) ErrorUnknownCallbackResult(v->GetGRFID(), CBID_VEHICLE_32DAY_CALLBACK, callback);
00840 }
00841 }
00842
00843
00844 v->OnNewDay();
00845 }
00846 }
00847
00848 void CallVehicleTicks()
00849 {
00850 _vehicles_to_autoreplace.Clear();
00851
00852 RunVehicleDayProc();
00853
00854 Station *st;
00855 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
00856
00857 Vehicle *v;
00858 FOR_ALL_VEHICLES(v) {
00859
00860 if (!v->Tick()) {
00861 assert(Vehicle::Get(vehicle_index) == NULL);
00862 continue;
00863 }
00864
00865 assert(Vehicle::Get(vehicle_index) == v);
00866
00867 switch (v->type) {
00868 default: break;
00869
00870 case VEH_TRAIN:
00871 case VEH_ROAD:
00872 case VEH_AIRCRAFT:
00873 case VEH_SHIP:
00874 if (v->vcache.cached_cargo_age_period != 0) {
00875 v->cargo_age_counter = min(v->cargo_age_counter, v->vcache.cached_cargo_age_period);
00876 if (--v->cargo_age_counter == 0) {
00877 v->cargo.AgeCargo();
00878 v->cargo_age_counter = v->vcache.cached_cargo_age_period;
00879 }
00880 }
00881
00882 if (v->type == VEH_TRAIN && Train::From(v)->IsWagon()) continue;
00883 if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
00884 if (v->type == VEH_ROAD && !RoadVehicle::From(v)->IsFrontEngine()) continue;
00885
00886 v->motion_counter += v->cur_speed;
00887
00888 if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
00889
00890
00891 if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
00892 }
00893 }
00894
00895 Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
00896 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
00897 v = it->first;
00898
00899 cur_company.Change(v->owner);
00900
00901
00902
00903
00904 if (it->second) v->vehstatus &= ~VS_STOPPED;
00905
00906
00907 int x = v->x_pos;
00908 int y = v->y_pos;
00909 int z = v->z_pos;
00910
00911 const Company *c = Company::Get(_current_company);
00912 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
00913 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
00914 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
00915
00916 if (!IsLocalCompany()) continue;
00917
00918 if (res.Succeeded()) {
00919 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
00920 continue;
00921 }
00922
00923 StringID error_message = res.GetErrorMessage();
00924 if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
00925
00926 if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
00927
00928 StringID message;
00929 if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
00930 message = error_message;
00931 } else {
00932 message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
00933 }
00934
00935 SetDParam(0, v->index);
00936 SetDParam(1, error_message);
00937 AddVehicleNewsItem(message, NS_ADVICE, v->index);
00938 }
00939
00940 cur_company.Restore();
00941 }
00942
00947 static void DoDrawVehicle(const Vehicle *v)
00948 {
00949 SpriteID image = v->cur_image;
00950 PaletteID pal = PAL_NONE;
00951
00952 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
00953
00954
00955 bool shadowed = (v->vehstatus & VS_SHADOW) != 0;
00956
00957 if (v->type == VEH_EFFECT) {
00958
00959
00960 TransparencyOption to = EffectVehicle::From(v)->GetTransparencyOption();
00961 if (to != TO_INVALID && (IsTransparencySet(to) || IsInvisibilitySet(to))) return;
00962 }
00963
00964 AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
00965 v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs);
00966 }
00967
00972 void ViewportAddVehicles(DrawPixelInfo *dpi)
00973 {
00974
00975 const int l = dpi->left;
00976 const int r = dpi->left + dpi->width;
00977 const int t = dpi->top;
00978 const int b = dpi->top + dpi->height;
00979
00980
00981 int xl, xu, yl, yu;
00982
00983 if (dpi->width + (70 * ZOOM_LVL_BASE) < (1 << (7 + 6 + ZOOM_LVL_SHIFT))) {
00984 xl = GB(l - (70 * ZOOM_LVL_BASE), 7 + ZOOM_LVL_SHIFT, 6);
00985 xu = GB(r, 7 + ZOOM_LVL_SHIFT, 6);
00986 } else {
00987
00988 xl = 0;
00989 xu = 0x3F;
00990 }
00991
00992 if (dpi->height + (70 * ZOOM_LVL_BASE) < (1 << (6 + 6 + ZOOM_LVL_SHIFT))) {
00993 yl = GB(t - (70 * ZOOM_LVL_BASE), 6 + ZOOM_LVL_SHIFT, 6) << 6;
00994 yu = GB(b, 6 + ZOOM_LVL_SHIFT, 6) << 6;
00995 } else {
00996
00997 yl = 0;
00998 yu = 0x3F << 6;
00999 }
01000
01001 for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
01002 for (int x = xl;; x = (x + 1) & 0x3F) {
01003 const Vehicle *v = _vehicle_viewport_hash[x + y];
01004
01005 while (v != NULL) {
01006 if (!(v->vehstatus & VS_HIDDEN) &&
01007 l <= v->coord.right &&
01008 t <= v->coord.bottom &&
01009 r >= v->coord.left &&
01010 b >= v->coord.top) {
01011 DoDrawVehicle(v);
01012 }
01013 v = v->hash_viewport_next;
01014 }
01015
01016 if (x == xu) break;
01017 }
01018
01019 if (y == yu) break;
01020 }
01021 }
01022
01030 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
01031 {
01032 Vehicle *found = NULL, *v;
01033 uint dist, best_dist = UINT_MAX;
01034
01035 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
01036
01037 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
01038 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
01039
01040 FOR_ALL_VEHICLES(v) {
01041 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
01042 x >= v->coord.left && x <= v->coord.right &&
01043 y >= v->coord.top && y <= v->coord.bottom) {
01044
01045 dist = max(
01046 abs(((v->coord.left + v->coord.right) >> 1) - x),
01047 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
01048 );
01049
01050 if (dist < best_dist) {
01051 found = v;
01052 best_dist = dist;
01053 }
01054 }
01055 }
01056
01057 return found;
01058 }
01059
01064 void DecreaseVehicleValue(Vehicle *v)
01065 {
01066 v->value -= v->value >> 8;
01067 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01068 }
01069
01070 static const byte _breakdown_chance[64] = {
01071 3, 3, 3, 3, 3, 3, 3, 3,
01072 4, 4, 5, 5, 6, 6, 7, 7,
01073 8, 8, 9, 9, 10, 10, 11, 11,
01074 12, 13, 13, 13, 13, 14, 15, 16,
01075 17, 19, 21, 25, 28, 31, 34, 37,
01076 40, 44, 48, 52, 56, 60, 64, 68,
01077 72, 80, 90, 100, 110, 120, 130, 140,
01078 150, 170, 190, 210, 230, 250, 250, 250,
01079 };
01080
01081 void CheckVehicleBreakdown(Vehicle *v)
01082 {
01083 int rel, rel_old;
01084
01085
01086 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
01087 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01088
01089 if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
01090 _settings_game.difficulty.vehicle_breakdowns < 1 ||
01091 v->cur_speed < 5 || _game_mode == GM_MENU) {
01092 return;
01093 }
01094
01095 uint32 r = Random();
01096
01097
01098 int chance = v->breakdown_chance + 1;
01099 if (Chance16I(1, 25, r)) chance += 25;
01100 v->breakdown_chance = min(255, chance);
01101
01102
01103 rel = v->reliability;
01104 if (v->type == VEH_SHIP) rel += 0x6666;
01105
01106
01107 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
01108
01109
01110 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
01111 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
01112 v->breakdown_delay = GB(r, 24, 7) + 0x80;
01113 v->breakdown_chance = 0;
01114 }
01115 }
01116
01123 bool Vehicle::HandleBreakdown()
01124 {
01125
01126
01127
01128
01129
01130 switch (this->breakdown_ctr) {
01131 case 0:
01132 return false;
01133
01134 case 2:
01135 this->breakdown_ctr = 1;
01136
01137 if (this->breakdowns_since_last_service != 255) {
01138 this->breakdowns_since_last_service++;
01139 }
01140
01141 if (this->type == VEH_AIRCRAFT) {
01142
01143 this->vehstatus |= VS_AIRCRAFT_BROKEN;
01144 } else {
01145 this->cur_speed = 0;
01146
01147 if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
01148 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
01149 (this->type == VEH_TRAIN ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
01150 (this->type == VEH_TRAIN ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
01151 }
01152
01153 if (!(this->vehstatus & VS_HIDDEN)) {
01154 EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
01155 if (u != NULL) u->animation_state = this->breakdown_delay * 2;
01156 }
01157 }
01158
01159 this->MarkDirty();
01160 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01161 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01162
01163
01164 case 1:
01165
01166 if (this->type == VEH_AIRCRAFT) return false;
01167
01168
01169 if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
01170 if (--this->breakdown_delay == 0) {
01171 this->breakdown_ctr = 0;
01172 this->MarkDirty();
01173 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01174 }
01175 }
01176 return true;
01177
01178 default:
01179 if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
01180 return false;
01181 }
01182 }
01183
01188 void AgeVehicle(Vehicle *v)
01189 {
01190 if (v->age < MAX_DAY) {
01191 v->age++;
01192 if (v->IsPrimaryVehicle() && v->age == VEHICLE_PROFIT_MIN_AGE + 1) GroupStatistics::VehicleReachedProfitAge(v);
01193 }
01194
01195 if (!v->IsPrimaryVehicle() && (v->type != VEH_TRAIN || !Train::From(v)->IsEngine())) return;
01196
01197 int age = v->age - v->max_age;
01198 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
01199 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
01200 v->reliability_spd_dec <<= 1;
01201 }
01202
01203 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01204
01205
01206 if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
01207
01208
01209 if (Company::Get(v->owner)->settings.engine_renew && v->GetEngine()->company_avail != 0) return;
01210
01211 StringID str;
01212 if (age == -DAYS_IN_LEAP_YEAR) {
01213 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
01214 } else if (age == 0) {
01215 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
01216 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
01217 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
01218 } else {
01219 return;
01220 }
01221
01222 SetDParam(0, v->index);
01223 AddVehicleNewsItem(str, NS_ADVICE, v->index);
01224 }
01225
01232 uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *colour)
01233 {
01234 int count = 0;
01235 int max = 0;
01236 int cars = 0;
01237 int unloading = 0;
01238 bool loading = false;
01239
01240 const Vehicle *u = v;
01241
01242 const Station *st = Station::GetIfValid(v->last_station_visited);
01243 assert(colour == NULL || st != NULL);
01244
01245
01246 for (; v != NULL; v = v->Next()) {
01247 count += v->cargo.OnboardCount();
01248 max += v->cargo_cap;
01249 if (v->cargo_cap != 0 && colour != NULL) {
01250 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
01251 loading |= !(u->current_order.GetLoadType() & OLFB_NO_LOAD) && st->goods[v->cargo_type].days_since_pickup != 255;
01252 cars++;
01253 }
01254 }
01255
01256 if (colour != NULL) {
01257 if (unloading == 0 && loading) {
01258 *colour = STR_PERCENT_UP;
01259 } else if (cars == unloading || !loading) {
01260 *colour = STR_PERCENT_DOWN;
01261 } else {
01262 *colour = STR_PERCENT_UP_DOWN;
01263 }
01264 }
01265
01266
01267 if (max == 0) return 100;
01268
01269
01270 return (count * 100) / max;
01271 }
01272
01277 void VehicleEnterDepot(Vehicle *v)
01278 {
01279
01280 assert(v == v->First());
01281
01282 switch (v->type) {
01283 case VEH_TRAIN: {
01284 Train *t = Train::From(v);
01285 SetWindowClassesDirty(WC_TRAINS_LIST);
01286
01287 SetDepotReservation(t->tile, false);
01288 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
01289
01290 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
01291 t->wait_counter = 0;
01292 t->force_proceed = TFP_NONE;
01293 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
01294 t->ConsistChanged(true);
01295 break;
01296 }
01297
01298 case VEH_ROAD:
01299 SetWindowClassesDirty(WC_ROADVEH_LIST);
01300 break;
01301
01302 case VEH_SHIP: {
01303 SetWindowClassesDirty(WC_SHIPS_LIST);
01304 Ship *ship = Ship::From(v);
01305 ship->state = TRACK_BIT_DEPOT;
01306 ship->UpdateCache();
01307 ship->UpdateViewport(true, true);
01308 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01309 break;
01310 }
01311
01312 case VEH_AIRCRAFT:
01313 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01314 HandleAircraftEnterHangar(Aircraft::From(v));
01315 break;
01316 default: NOT_REACHED();
01317 }
01318 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01319
01320 if (v->type != VEH_TRAIN) {
01321
01322
01323 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01324 }
01325 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01326
01327 v->vehstatus |= VS_HIDDEN;
01328 v->cur_speed = 0;
01329
01330 VehicleServiceInDepot(v);
01331
01332 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01333
01334 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01335 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01336
01337 const Order *real_order = v->GetOrder(v->cur_real_order_index);
01338 Order t = v->current_order;
01339 v->current_order.MakeDummy();
01340
01341
01342
01343 if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
01344 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
01345 (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
01346
01347 return;
01348 }
01349
01350 if (t.IsRefit()) {
01351 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01352 CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
01353 cur_company.Restore();
01354
01355 if (cost.Failed()) {
01356 _vehicles_to_autoreplace[v] = false;
01357 if (v->owner == _local_company) {
01358
01359 SetDParam(0, v->index);
01360 AddVehicleNewsItem(STR_NEWS_ORDER_REFIT_FAILED, NS_ADVICE, v->index);
01361 }
01362 } else if (cost.GetCost() != 0) {
01363 v->profit_this_year -= cost.GetCost() << 8;
01364 if (v->owner == _local_company) {
01365 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01366 }
01367 }
01368 }
01369
01370 if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01371
01372 v->DeleteUnreachedImplicitOrders();
01373 UpdateVehicleTimetable(v, true);
01374 v->IncrementImplicitOrderIndex();
01375 }
01376 if (t.GetDepotActionType() & ODATFB_HALT) {
01377
01378 _vehicles_to_autoreplace[v] = false;
01379 if (v->owner == _local_company) {
01380 SetDParam(0, v->index);
01381 AddVehicleNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, NS_ADVICE, v->index);
01382 }
01383 AI::NewEvent(v->owner, new ScriptEventVehicleWaitingInDepot(v->index));
01384 }
01385 }
01386 }
01387
01388
01394 void VehicleUpdatePosition(Vehicle *v)
01395 {
01396 UpdateVehicleTileHash(v, false);
01397 }
01398
01405 void VehicleUpdateViewport(Vehicle *v, bool dirty)
01406 {
01407 int img = v->cur_image;
01408 Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
01409 const Sprite *spr = GetSprite(img, ST_NORMAL);
01410
01411 pt.x += spr->x_offs;
01412 pt.y += spr->y_offs;
01413
01414 UpdateVehicleViewportHash(v, pt.x, pt.y);
01415
01416 Rect old_coord = v->coord;
01417 v->coord.left = pt.x;
01418 v->coord.top = pt.y;
01419 v->coord.right = pt.x + spr->width + 2 * ZOOM_LVL_BASE;
01420 v->coord.bottom = pt.y + spr->height + 2 * ZOOM_LVL_BASE;
01421
01422 if (dirty) {
01423 MarkAllViewportsDirty(
01424 min(old_coord.left, v->coord.left),
01425 min(old_coord.top, v->coord.top),
01426 max(old_coord.right, v->coord.right) + 1 * ZOOM_LVL_BASE,
01427 max(old_coord.bottom, v->coord.bottom) + 1 * ZOOM_LVL_BASE
01428 );
01429 }
01430 }
01431
01436 void VehicleUpdatePositionAndViewport(Vehicle *v)
01437 {
01438 VehicleUpdatePosition(v);
01439 VehicleUpdateViewport(v, true);
01440 }
01441
01446 void MarkSingleVehicleDirty(const Vehicle *v)
01447 {
01448 MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1 * ZOOM_LVL_BASE, v->coord.bottom + 1 * ZOOM_LVL_BASE);
01449 }
01450
01456 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01457 {
01458 static const int8 _delta_coord[16] = {
01459 -1,-1,-1, 0, 1, 1, 1, 0,
01460 -1, 0, 1, 1, 1, 0,-1,-1,
01461 };
01462
01463 int x = v->x_pos + _delta_coord[v->direction];
01464 int y = v->y_pos + _delta_coord[v->direction + 8];
01465
01466 GetNewVehiclePosResult gp;
01467 gp.x = x;
01468 gp.y = y;
01469 gp.old_tile = v->tile;
01470 gp.new_tile = TileVirtXY(x, y);
01471 return gp;
01472 }
01473
01474 static const Direction _new_direction_table[] = {
01475 DIR_N, DIR_NW, DIR_W,
01476 DIR_NE, DIR_SE, DIR_SW,
01477 DIR_E, DIR_SE, DIR_S
01478 };
01479
01480 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01481 {
01482 int i = 0;
01483
01484 if (y >= v->y_pos) {
01485 if (y != v->y_pos) i += 3;
01486 i += 3;
01487 }
01488
01489 if (x >= v->x_pos) {
01490 if (x != v->x_pos) i++;
01491 i++;
01492 }
01493
01494 Direction dir = v->direction;
01495
01496 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01497 if (dirdiff == DIRDIFF_SAME) return dir;
01498 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01499 }
01500
01510 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01511 {
01512 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01513 }
01514
01522 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01523 {
01524
01525 const Vehicle *v;
01526 FOR_ALL_VEHICLES(v) {
01527 if (v->type == type && v->owner == owner) {
01528 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01529 }
01530 }
01531
01532 if (this->maxid == 0) return;
01533
01534
01535
01536
01537 this->cache = CallocT<bool>(this->maxid + 2);
01538
01539
01540 FOR_ALL_VEHICLES(v) {
01541 if (v->type == type && v->owner == owner) {
01542 this->cache[v->unitnumber] = true;
01543 }
01544 }
01545 }
01546
01548 UnitID FreeUnitIDGenerator::NextID()
01549 {
01550 if (this->maxid <= this->curid) return ++this->curid;
01551
01552 while (this->cache[++this->curid]) { }
01553
01554 return this->curid;
01555 }
01556
01562 UnitID GetFreeUnitNumber(VehicleType type)
01563 {
01564
01565 uint max_veh;
01566 switch (type) {
01567 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
01568 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
01569 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
01570 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
01571 default: NOT_REACHED();
01572 }
01573
01574 const Company *c = Company::Get(_current_company);
01575 if (c->group_all[type].num_vehicle >= max_veh) return UINT16_MAX;
01576
01577 FreeUnitIDGenerator gen(type, _current_company);
01578
01579 return gen.NextID();
01580 }
01581
01582
01591 bool CanBuildVehicleInfrastructure(VehicleType type)
01592 {
01593 assert(IsCompanyBuildableVehicleType(type));
01594
01595 if (!Company::IsValidID(_local_company)) return false;
01596 if (!_settings_client.gui.disable_unsuitable_building) return true;
01597
01598 UnitID max;
01599 switch (type) {
01600 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
01601 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
01602 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
01603 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01604 default: NOT_REACHED();
01605 }
01606
01607
01608 if (max > 0) {
01609
01610 const Engine *e;
01611 FOR_ALL_ENGINES_OF_TYPE(e, type) {
01612 if (HasBit(e->company_avail, _local_company)) return true;
01613 }
01614 return false;
01615 }
01616
01617
01618 const Vehicle *v;
01619 FOR_ALL_VEHICLES(v) {
01620 if (v->owner == _local_company && v->type == type) return true;
01621 }
01622
01623 return false;
01624 }
01625
01626
01634 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
01635 {
01636 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01637 const Engine *e = Engine::Get(engine_type);
01638 switch (e->type) {
01639 default: NOT_REACHED();
01640 case VEH_TRAIN:
01641 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
01642
01643
01644 engine_type = parent_engine_type;
01645 e = Engine::Get(engine_type);
01646
01647 }
01648
01649 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01650 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01651 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
01652 if (!CargoSpec::Get(cargo_type)->is_freight) {
01653 if (parent_engine_type == INVALID_ENGINE) {
01654 return LS_PASSENGER_WAGON_STEAM;
01655 } else {
01656 switch (RailVehInfo(parent_engine_type)->engclass) {
01657 default: NOT_REACHED();
01658 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
01659 case EC_DIESEL: return LS_PASSENGER_WAGON_DIESEL;
01660 case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
01661 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
01662 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
01663 }
01664 }
01665 } else {
01666 return LS_FREIGHT_WAGON;
01667 }
01668 } else {
01669 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
01670
01671 switch (e->u.rail.engclass) {
01672 default: NOT_REACHED();
01673 case EC_STEAM: return LS_STEAM;
01674 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
01675 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
01676 case EC_MONORAIL: return LS_MONORAIL;
01677 case EC_MAGLEV: return LS_MAGLEV;
01678 }
01679 }
01680
01681 case VEH_ROAD:
01682
01683 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01684 engine_type = parent_engine_type;
01685 e = Engine::Get(engine_type);
01686 cargo_type = v->First()->cargo_type;
01687 }
01688 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01689 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01690
01691
01692 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
01693
01694 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01695 } else {
01696
01697 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01698 }
01699
01700 case VEH_SHIP:
01701 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01702 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01703 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01704
01705 case VEH_AIRCRAFT:
01706 switch (e->u.air.subtype) {
01707 case AIR_HELI: return LS_HELICOPTER;
01708 case AIR_CTOL: return LS_SMALL_PLANE;
01709 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
01710 default: NOT_REACHED();
01711 }
01712 }
01713 }
01714
01724 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
01725 {
01726 const Company *c = Company::Get(company);
01727 LiveryScheme scheme = LS_DEFAULT;
01728
01729
01730
01731 if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
01732
01733 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
01734
01735
01736 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01737 }
01738
01739 return &c->livery[scheme];
01740 }
01741
01742
01743 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01744 {
01745 PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01746
01747
01748 if (map != PAL_NONE) return map;
01749
01750 const Engine *e = Engine::Get(engine_type);
01751
01752
01753 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
01754 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01755
01756 if (callback != CALLBACK_FAILED) {
01757 assert_compile(PAL_NONE == 0);
01758 map = GB(callback, 0, 14);
01759
01760
01761 if (!HasBit(callback, 14)) {
01762
01763 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01764 return map;
01765 }
01766 }
01767 }
01768
01769 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
01770
01771 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
01772
01773
01774 if (!Company::IsValidID(company)) return map;
01775
01776 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
01777
01778 map += livery->colour1;
01779 if (twocc) map += livery->colour2 * 16;
01780
01781
01782 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01783 return map;
01784 }
01785
01792 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
01793 {
01794 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01795 }
01796
01802 PaletteID GetVehiclePalette(const Vehicle *v)
01803 {
01804 if (v->IsGroundVehicle()) {
01805 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
01806 }
01807
01808 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01809 }
01810
01814 void Vehicle::DeleteUnreachedImplicitOrders()
01815 {
01816 if (this->IsGroundVehicle()) {
01817 uint16 &gv_flags = this->GetGroundVehicleFlags();
01818 if (HasBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)) {
01819
01820 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
01821 this->cur_implicit_order_index = this->cur_real_order_index;
01822 InvalidateVehicleOrder(this, 0);
01823 return;
01824 }
01825 }
01826
01827 const Order *order = this->GetOrder(this->cur_implicit_order_index);
01828 while (order != NULL) {
01829 if (this->cur_implicit_order_index == this->cur_real_order_index) break;
01830
01831 if (order->IsType(OT_IMPLICIT)) {
01832
01833 order = order->next;
01834 DeleteOrder(this, this->cur_implicit_order_index);
01835 } else {
01836
01837 order = order->next;
01838 this->cur_implicit_order_index++;
01839 }
01840
01841
01842 if (order == NULL) {
01843 order = this->GetOrder(0);
01844 this->cur_implicit_order_index = 0;
01845 }
01846 }
01847 }
01848
01853 void Vehicle::BeginLoading()
01854 {
01855 assert(IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP);
01856
01857 if (this->current_order.IsType(OT_GOTO_STATION) &&
01858 this->current_order.GetDestination() == this->last_station_visited) {
01859 this->DeleteUnreachedImplicitOrders();
01860
01861
01862 this->current_order.MakeLoading(true);
01863 UpdateVehicleTimetable(this, true);
01864
01865
01866
01867
01868
01869
01870 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
01871
01872 } else {
01873
01874
01875
01876
01877
01878 Order *in_list = this->GetOrder(this->cur_implicit_order_index);
01879 if (this->IsGroundVehicle() && in_list != NULL &&
01880 (!in_list->IsType(OT_IMPLICIT) ||
01881 in_list->GetDestination() != this->last_station_visited)) {
01882 bool suppress_implicit_orders = HasBit(this->GetGroundVehicleFlags(), GVF_SUPPRESS_IMPLICIT_ORDERS);
01883
01884 Order *prev_order = this->cur_implicit_order_index > 0 ? this->GetOrder(this->cur_implicit_order_index - 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : NULL);
01885 if (prev_order == NULL ||
01886 (!prev_order->IsType(OT_IMPLICIT) && !prev_order->IsType(OT_GOTO_STATION)) ||
01887 prev_order->GetDestination() != this->last_station_visited) {
01888
01889
01890
01891 int target_index = this->cur_implicit_order_index;
01892 bool found = false;
01893 while (target_index != this->cur_real_order_index) {
01894 const Order *order = this->GetOrder(target_index);
01895 if (order->IsType(OT_IMPLICIT) && order->GetDestination() == this->last_station_visited) {
01896 found = true;
01897 break;
01898 }
01899 target_index++;
01900 if (target_index >= this->orders.list->GetNumOrders()) target_index = 0;
01901 assert(target_index != this->cur_implicit_order_index);
01902 }
01903
01904 if (found) {
01905 if (suppress_implicit_orders) {
01906
01907 this->cur_implicit_order_index = target_index;
01908 InvalidateVehicleOrder(this, 0);
01909 } else {
01910
01911 const Order *order = this->GetOrder(this->cur_implicit_order_index);
01912 while (!order->IsType(OT_IMPLICIT) || order->GetDestination() != this->last_station_visited) {
01913 if (order->IsType(OT_IMPLICIT)) {
01914
01915 order = order->next;
01916 DeleteOrder(this, this->cur_implicit_order_index);
01917 } else {
01918
01919 order = order->next;
01920 this->cur_implicit_order_index++;
01921 }
01922
01923
01924 if (order == NULL) {
01925 order = this->GetOrder(0);
01926 this->cur_implicit_order_index = 0;
01927 }
01928 assert(order != NULL);
01929 }
01930 }
01931 } else if (!suppress_implicit_orders && this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID && Order::CanAllocateItem()) {
01932
01933 Order *implicit_order = new Order();
01934 implicit_order->MakeImplicit(this->last_station_visited);
01935 InsertOrder(this, implicit_order, this->cur_implicit_order_index);
01936 if (this->cur_implicit_order_index > 0) --this->cur_implicit_order_index;
01937
01938
01939
01940 uint16 &gv_flags = this->GetGroundVehicleFlags();
01941 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
01942 }
01943 }
01944 }
01945 this->current_order.MakeLoading(false);
01946 }
01947
01948 if (this->last_loading_station != INVALID_STATION &&
01949 this->last_loading_station != this->last_station_visited &&
01950 ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
01951 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0)) {
01952 IncreaseStats(Station::Get(this->last_loading_station), this, this->last_station_visited);
01953 }
01954
01955 PrepareUnload(this);
01956
01957 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
01958 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
01959 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01960 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
01961
01962 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
01963 this->cur_speed = 0;
01964 this->MarkDirty();
01965 }
01966
01971 void Vehicle::CancelReservation(StationID next, Station *st)
01972 {
01973 for (Vehicle *v = this; v != NULL; v = v->next) {
01974 VehicleCargoList &cargo = v->cargo;
01975 if (cargo.ReservedCount() > 0) {
01976 DEBUG(misc, 1, "cancelling cargo reservation");
01977 GoodsEntry &ge = st->goods[v->cargo_type];
01978 cargo.Unreserve(next, &ge.cargo);
01979 SetBit(ge.acceptance_pickup, GoodsEntry::GES_PICKUP);
01980 }
01981 }
01982 }
01983
01988 void Vehicle::LeaveStation()
01989 {
01990 assert(this->current_order.IsType(OT_LOADING));
01991
01992 delete this->cargo_payment;
01993
01994
01995 if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
01996
01997 if ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
01998 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
01999 if (this->current_order.CanLeaveWithCargo(this->last_loading_station != INVALID_STATION)) {
02000
02001
02002
02003
02004 this->RefreshNextHopsStats();
02005
02006
02007 this->last_loading_station = this->last_station_visited;
02008 } else {
02009
02010
02011
02012 this->last_loading_station = INVALID_STATION;
02013 }
02014 }
02015
02016 this->current_order.MakeLeaveStation();
02017 Station *st = Station::Get(this->last_station_visited);
02018 this->CancelReservation(INVALID_STATION, st);
02019 st->loading_vehicles.remove(this);
02020
02021 HideFillingPercent(&this->fill_percent_te_id);
02022
02023 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
02024
02025 if (IsTileType(this->tile, MP_STATION)) TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
02026
02027 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
02028 }
02029 }
02030
02037 void Vehicle::RefreshNextHopsStats()
02038 {
02039
02040 SmallMap<CargoID, uint, 1> capacities;
02041 for (Vehicle *v = this; v != NULL; v = v->Next()) {
02042 v->refit_cap = v->cargo_cap;
02043 if (v->refit_cap == 0) continue;
02044 SmallPair<CargoID, uint> *i = capacities.Find(v->cargo_type);
02045 if (i == capacities.End()) {
02046
02047 i = capacities.Append();
02048 i->first = v->cargo_type;
02049 i->second = v->cargo_cap;
02050 } else {
02051 i->second += v->cargo_cap;
02052 }
02053 }
02054
02055
02056 if (this->orders.list == NULL) return;
02057
02058 uint hops = 0;
02059 const Order *first = this->orders.list->GetNextStoppingOrder(this,
02060 this->GetOrder(this->cur_implicit_order_index), hops);
02061 const Order *cur = first;
02062 const Order *next = first;
02063 while (next != NULL && cur->CanLeaveWithCargo(true)) {
02064 next = this->orders.list->GetNextStoppingOrder(this,
02065 this->orders.list->GetNext(next), ++hops);
02066 if (next == NULL) break;
02067
02068 if (next->IsType(OT_GOTO_DEPOT)) {
02069
02070 CargoID new_cid = next->GetRefitCargo();
02071 byte new_subtype = next->GetRefitSubtype();
02072 for (Vehicle *v = this; v != NULL; v = v->Next()) {
02073 const Engine *e = Engine::Get(v->engine_type);
02074 if (!HasBit(e->info.refit_mask, new_cid)) continue;
02075
02076
02077 CargoID temp_cid = v->cargo_type;
02078 byte temp_subtype = v->cargo_subtype;
02079 v->cargo_type = new_cid;
02080 v->cargo_subtype = new_subtype;
02081
02082 uint16 mail_capacity = 0;
02083 uint amount = e->DetermineCapacity(v, &mail_capacity);
02084
02085
02086 v->cargo_type = temp_cid;
02087 v->cargo_subtype = temp_subtype;
02088
02089
02090 if (new_cid != v->cargo_type && v->refit_cap > 0) {
02091 capacities[v->cargo_type] -= v->refit_cap;
02092 v->refit_cap = 0;
02093 } else if (amount < v->refit_cap) {
02094 capacities[v->cargo_type] -= v->refit_cap - amount;
02095 v->refit_cap = amount;
02096 }
02097
02098
02099 if (v->type == VEH_AIRCRAFT) {
02100 Vehicle *u = v->Next();
02101 if (mail_capacity < u->refit_cap) {
02102 capacities[u->cargo_type] -= u->refit_cap - mail_capacity;
02103 u->refit_cap = mail_capacity;
02104 }
02105 break;
02106 }
02107 if (v->type == VEH_SHIP) break;
02108 }
02109 } else {
02110 StationID next_station = next->GetDestination();
02111 Station *st = Station::GetIfValid(cur->GetDestination());
02112 if (st != NULL && next_station != INVALID_STATION && next_station != st->index) {
02113 for (const SmallPair<CargoID, uint> *i = capacities.Begin(); i != capacities.End(); ++i) {
02114
02115 if (i->second > 0) IncreaseStats(st, i->first, next_station, i->second, UINT_MAX);
02116 }
02117 }
02118 cur = next;
02119 if (cur == first) break;
02120 }
02121 }
02122
02123 for (Vehicle *v = this; v != NULL; v = v->Next()) v->refit_cap = v->cargo_cap;
02124 }
02125
02131 void Vehicle::HandleLoading(bool mode)
02132 {
02133 switch (this->current_order.GetType()) {
02134 case OT_LOADING: {
02135 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
02136
02137
02138 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
02139
02140 this->PlayLeaveStationSound();
02141
02142 this->LeaveStation();
02143
02144
02145 const Order *order = this->GetOrder(this->cur_implicit_order_index);
02146 if (order == NULL ||
02147 (!order->IsType(OT_IMPLICIT) && !order->IsType(OT_GOTO_STATION)) ||
02148 order->GetDestination() != this->last_station_visited) {
02149 return;
02150 }
02151 break;
02152 }
02153
02154 case OT_DUMMY: break;
02155
02156 default: return;
02157 }
02158
02159 this->IncrementImplicitOrderIndex();
02160 }
02161
02166 void Vehicle::GetConsistFreeCapacities(SmallMap<CargoID, uint> &capacities) const
02167 {
02168 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
02169 if (v->cargo_cap == 0) continue;
02170 SmallPair<CargoID, uint> *pair = capacities.Find(v->cargo_type);
02171 if (pair == capacities.End()) {
02172 pair = capacities.Append();
02173 pair->first = v->cargo_type;
02174 pair->second = v->cargo_cap - v->cargo.Count();
02175 } else {
02176 pair->second += v->cargo_cap - v->cargo.Count();
02177 }
02178 }
02179 }
02180
02181 uint Vehicle::GetConsistTotalCapacity() const
02182 {
02183 uint result = 0;
02184 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
02185 result += v->cargo_cap;
02186 }
02187 return result;
02188 }
02189
02196 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
02197 {
02198 CommandCost ret = CheckOwnership(this->owner);
02199 if (ret.Failed()) return ret;
02200
02201 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
02202 if (this->IsStoppedInDepot()) return CMD_ERROR;
02203
02204 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
02205 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
02206 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
02207
02208
02209
02210 if (flags & DC_EXEC) {
02211 this->current_order.SetDepotOrderType(ODTF_MANUAL);
02212 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
02213 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02214 }
02215 return CommandCost();
02216 }
02217
02218 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR;
02219 if (flags & DC_EXEC) {
02220
02221
02222 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
02223
02224 if (this->IsGroundVehicle()) {
02225 uint16 &gv_flags = this->GetGroundVehicleFlags();
02226 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02227 }
02228
02229 this->current_order.MakeDummy();
02230 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02231 }
02232 return CommandCost();
02233 }
02234
02235 TileIndex location;
02236 DestinationID destination;
02237 bool reverse;
02238 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};
02239 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
02240
02241 if (flags & DC_EXEC) {
02242 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
02243
02244 if (this->IsGroundVehicle()) {
02245 uint16 &gv_flags = this->GetGroundVehicleFlags();
02246 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02247 }
02248
02249 this->dest_tile = location;
02250 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
02251 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
02252 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02253
02254
02255 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
02256
02257 if (this->type == VEH_AIRCRAFT) {
02258 Aircraft *a = Aircraft::From(this);
02259 if (a->state == FLYING && a->targetairport != destination) {
02260
02261 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
02262 AircraftNextAirportPos_and_Order(a);
02263 }
02264 }
02265 }
02266
02267 return CommandCost();
02268
02269 }
02270
02275 void Vehicle::UpdateVisualEffect(bool allow_power_change)
02276 {
02277 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02278 const Engine *e = this->GetEngine();
02279
02280
02281 byte visual_effect;
02282 switch (e->type) {
02283 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
02284 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
02285 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
02286 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
02287 }
02288
02289
02290 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
02291 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
02292
02293 if (callback != CALLBACK_FAILED) {
02294 if (callback >= 0x100 && e->GetGRF()->grf_version >= 8) ErrorUnknownCallbackResult(e->GetGRFID(), CBID_VEHICLE_VISUAL_EFFECT, callback);
02295
02296 callback = GB(callback, 0, 8);
02297
02298
02299 if (callback == VE_DEFAULT) {
02300 assert(HasBit(callback, VE_DISABLE_EFFECT));
02301 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
02302 }
02303 visual_effect = callback;
02304 }
02305 }
02306
02307
02308 if (visual_effect == VE_DEFAULT ||
02309 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
02310
02311
02312 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
02313 if (visual_effect == VE_DEFAULT) {
02314 visual_effect = 1 << VE_DISABLE_EFFECT;
02315 } else {
02316 SetBit(visual_effect, VE_DISABLE_EFFECT);
02317 }
02318 } else {
02319 if (visual_effect == VE_DEFAULT) {
02320
02321 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
02322 }
02323 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
02324 }
02325 }
02326
02327 this->vcache.cached_vis_effect = visual_effect;
02328
02329 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
02330 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02331 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
02332 }
02333 }
02334
02335 static const int8 _vehicle_smoke_pos[8] = {
02336 1, 1, 1, 0, -1, -1, -1, 0
02337 };
02338
02343 void Vehicle::ShowVisualEffect() const
02344 {
02345 assert(this->IsPrimaryVehicle());
02346 bool sound = false;
02347
02348
02349
02350
02351
02352
02353 if (_settings_game.vehicle.smoke_amount == 0 ||
02354 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
02355 this->cur_speed < 2) {
02356 return;
02357 }
02358 if (this->type == VEH_TRAIN) {
02359 const Train *t = Train::From(this);
02360
02361
02362
02363
02364 if (HasBit(t->flags, VRF_REVERSING) ||
02365 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
02366 t->cur_speed >= t->Train::GetCurrentMaxSpeed())) {
02367 return;
02368 }
02369 }
02370
02371 const Vehicle *v = this;
02372
02373 do {
02374 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
02375 byte effect_type = GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
02376 bool disable_effect = HasBit(v->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
02377
02378
02379
02380
02381
02382
02383
02384
02385 if (disable_effect ||
02386 v->vehstatus & VS_HIDDEN ||
02387 (MayHaveBridgeAbove(v->tile) && IsBridgeAbove(v->tile)) ||
02388 IsDepotTile(v->tile) ||
02389 IsTunnelTile(v->tile) ||
02390 (v->type == VEH_TRAIN &&
02391 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
02392 continue;
02393 }
02394
02395
02396
02397
02398 if (v->type == VEH_TRAIN) effect_offset += (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
02399
02400 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
02401 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
02402
02403 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
02404 x = -x;
02405 y = -y;
02406 }
02407
02408 switch (effect_type) {
02409 case VE_TYPE_STEAM:
02410
02411
02412
02413
02414
02415 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / this->vcache.cached_max_speed))) == 0) {
02416 CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
02417 sound = true;
02418 }
02419 break;
02420
02421 case VE_TYPE_DIESEL: {
02422
02423
02424
02425
02426
02427
02428
02429
02430
02431
02432
02433 int power_weight_effect = 0;
02434 if (v->type == VEH_TRAIN) {
02435 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
02436 }
02437 if (this->cur_speed < (this->vcache.cached_max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
02438 Chance16((64 - ((this->cur_speed << 5) / this->vcache.cached_max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
02439 CreateEffectVehicleRel(v, x, y, 10, EV_DIESEL_SMOKE);
02440 sound = true;
02441 }
02442 break;
02443 }
02444
02445 case VE_TYPE_ELECTRIC:
02446
02447
02448
02449
02450
02451
02452 if (GB(v->tick_counter, 0, 2) == 0 &&
02453 Chance16((6 - ((this->cur_speed << 2) / this->vcache.cached_max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
02454 CreateEffectVehicleRel(v, x, y, 10, EV_ELECTRIC_SPARK);
02455 sound = true;
02456 }
02457 break;
02458
02459 default:
02460 break;
02461 }
02462 } while ((v = v->Next()) != NULL);
02463
02464 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
02465 }
02466
02471 void Vehicle::SetNext(Vehicle *next)
02472 {
02473 assert(this != next);
02474
02475 if (this->next != NULL) {
02476
02477 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02478 v->first = this->next;
02479 }
02480 this->next->previous = NULL;
02481 }
02482
02483 this->next = next;
02484
02485 if (this->next != NULL) {
02486
02487 if (this->next->previous != NULL) this->next->previous->next = NULL;
02488 this->next->previous = this;
02489 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02490 v->first = this->first;
02491 }
02492 }
02493 }
02494
02500 void Vehicle::AddToShared(Vehicle *shared_chain)
02501 {
02502 assert(this->previous_shared == NULL && this->next_shared == NULL);
02503
02504 if (shared_chain->orders.list == NULL) {
02505 assert(shared_chain->previous_shared == NULL);
02506 assert(shared_chain->next_shared == NULL);
02507 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
02508 }
02509
02510 this->next_shared = shared_chain->next_shared;
02511 this->previous_shared = shared_chain;
02512
02513 shared_chain->next_shared = this;
02514
02515 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
02516
02517 shared_chain->orders.list->AddVehicle(this);
02518 }
02519
02523 void Vehicle::RemoveFromShared()
02524 {
02525
02526
02527 bool were_first = (this->FirstShared() == this);
02528 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
02529
02530 this->orders.list->RemoveVehicle(this);
02531
02532 if (!were_first) {
02533
02534 this->previous_shared->next_shared = this->NextShared();
02535 }
02536
02537 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
02538
02539
02540 if (this->orders.list->GetNumVehicles() == 1) {
02541
02542 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
02543 InvalidateVehicleOrder(this->FirstShared(), 0);
02544 } else if (were_first) {
02545
02546
02547 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
02548 }
02549
02550 this->next_shared = NULL;
02551 this->previous_shared = NULL;
02552 }
02553
02554 void VehiclesYearlyLoop()
02555 {
02556 Vehicle *v;
02557 FOR_ALL_VEHICLES(v) {
02558 if (v->IsPrimaryVehicle()) {
02559
02560 Money profit = v->GetDisplayProfitThisYear();
02561 if (v->age >= 730 && profit < 0) {
02562 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
02563 SetDParam(0, v->index);
02564 SetDParam(1, profit);
02565 AddVehicleNewsItem(
02566 STR_NEWS_VEHICLE_IS_UNPROFITABLE,
02567 NS_ADVICE,
02568 v->index
02569 );
02570 }
02571 AI::NewEvent(v->owner, new ScriptEventVehicleUnprofitable(v->index));
02572 }
02573
02574 v->profit_last_year = v->profit_this_year;
02575 v->profit_this_year = 0;
02576 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
02577 }
02578 }
02579 GroupStatistics::UpdateProfits();
02580 SetWindowClassesDirty(WC_TRAINS_LIST);
02581 SetWindowClassesDirty(WC_SHIPS_LIST);
02582 SetWindowClassesDirty(WC_ROADVEH_LIST);
02583 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
02584 }
02585
02586
02596 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
02597 {
02598 const Engine *e = Engine::GetIfValid(engine_type);
02599 assert(e != NULL);
02600
02601 switch (e->type) {
02602 case VEH_TRAIN:
02603 return (st->facilities & FACIL_TRAIN) != 0;
02604
02605 case VEH_ROAD:
02606
02607
02608
02609 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
02610
02611 case VEH_SHIP:
02612 return (st->facilities & FACIL_DOCK) != 0;
02613
02614 case VEH_AIRCRAFT:
02615 return (st->facilities & FACIL_AIRPORT) != 0 &&
02616 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
02617
02618 default:
02619 return false;
02620 }
02621 }
02622
02629 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
02630 {
02631 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
02632
02633 return CanVehicleUseStation(v->engine_type, st);
02634 }
02635
02641 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
02642 {
02643 assert(this->IsGroundVehicle());
02644 if (this->type == VEH_TRAIN) {
02645 return &Train::From(this)->gcache;
02646 } else {
02647 return &RoadVehicle::From(this)->gcache;
02648 }
02649 }
02650
02656 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
02657 {
02658 assert(this->IsGroundVehicle());
02659 if (this->type == VEH_TRAIN) {
02660 return &Train::From(this)->gcache;
02661 } else {
02662 return &RoadVehicle::From(this)->gcache;
02663 }
02664 }
02665
02671 uint16 &Vehicle::GetGroundVehicleFlags()
02672 {
02673 assert(this->IsGroundVehicle());
02674 if (this->type == VEH_TRAIN) {
02675 return Train::From(this)->gv_flags;
02676 } else {
02677 return RoadVehicle::From(this)->gv_flags;
02678 }
02679 }
02680
02686 const uint16 &Vehicle::GetGroundVehicleFlags() const
02687 {
02688 assert(this->IsGroundVehicle());
02689 if (this->type == VEH_TRAIN) {
02690 return Train::From(this)->gv_flags;
02691 } else {
02692 return RoadVehicle::From(this)->gv_flags;
02693 }
02694 }
02695
02704 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
02705 {
02706 if (v->type == VEH_TRAIN) {
02707 Train *u = Train::From(v);
02708
02709 u = u->GetFirstEnginePart();
02710
02711
02712 for (; u != NULL && num_vehicles > 0; num_vehicles--) {
02713 do {
02714
02715 set.Include(u->index);
02716
02717
02718 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
02719
02720 u = u->Next();
02721 } while (u != NULL && u->IsArticulatedPart());
02722 }
02723 }
02724 }