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