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
00073 bool Vehicle::NeedsAutorenewing(const Company *c, bool use_renew_setting) const
00074 {
00075
00076
00077
00078
00079 assert(c == Company::Get(this->owner));
00080
00081 if (use_renew_setting && !c->settings.engine_renew) return false;
00082 if (this->age - this->max_age < (c->settings.engine_renew_months * 30)) return false;
00083
00084
00085 if (this->type == VEH_TRAIN && !Train::From(this)->IsEngine()) return false;
00086
00087 return true;
00088 }
00089
00090 void VehicleServiceInDepot(Vehicle *v)
00091 {
00092 v->date_of_last_service = _date;
00093 v->breakdowns_since_last_service = 0;
00094 v->reliability = v->GetEngine()->reliability;
00095
00096 v->breakdown_chance /= 4;
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 bool replace_when_old = false;
00136 EngineID new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id, &replace_when_old);
00137
00138
00139 if (new_engine == INVALID_ENGINE || !HasBit(Engine::Get(new_engine)->company_avail, v->owner)) continue;
00140
00141 if (replace_when_old && !v->NeedsAutorenewing(c, false)) continue;
00142
00143
00144 uint32 available_cargo_types, union_mask;
00145 GetArticulatedRefitMasks(new_engine, true, &union_mask, &available_cargo_types);
00146
00147 if (union_mask != 0) {
00148 CargoID cargo_type;
00149
00150 if (IsArticulatedVehicleCarryingDifferentCargoes(v, &cargo_type)) continue;
00151
00152
00153 if (cargo_type != CT_INVALID) {
00154
00155 if (!HasBit(available_cargo_types, cargo_type)) continue;
00156 }
00157 }
00158
00159
00160
00161 pending_replace = true;
00162 needed_money += 2 * Engine::Get(new_engine)->GetCost();
00163 if (needed_money > c->money) return false;
00164 }
00165
00166 return pending_replace;
00167 }
00168
00174 bool Vehicle::NeedsAutomaticServicing() const
00175 {
00176 if (this->HasDepotOrder()) return false;
00177 if (this->current_order.IsType(OT_LOADING)) return false;
00178 if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
00179 return NeedsServicing();
00180 }
00181
00182 uint Vehicle::Crash(bool flooded)
00183 {
00184 assert((this->vehstatus & VS_CRASHED) == 0);
00185 assert(this->Previous() == NULL);
00186
00187 uint pass = 0;
00188
00189 if (this->IsPrimaryVehicle()) this->vehstatus |= VS_STOPPED;
00190
00191 for (Vehicle *v = this; v != NULL; v = v->Next()) {
00192 if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo.Count();
00193 v->vehstatus |= VS_CRASHED;
00194 MarkSingleVehicleDirty(v);
00195 }
00196
00197
00198 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00199 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
00200 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
00201 SetWindowDirty(WC_VEHICLE_DEPOT, this->tile);
00202
00203 delete this->cargo_payment;
00204 this->cargo_payment = NULL;
00205
00206 return RandomRange(pass + 1);
00207 }
00208
00209
00218 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
00219 {
00220 const Engine *e = Engine::Get(engine);
00221 GRFConfig *grfconfig = GetGRFConfig(e->GetGRFID());
00222
00223 if (!HasBit(grfconfig->grf_bugs, bug_type)) {
00224 SetBit(grfconfig->grf_bugs, bug_type);
00225 SetDParamStr(0, grfconfig->GetName());
00226 SetDParam(1, engine);
00227 ShowErrorMessage(part1, part2, WL_CRITICAL);
00228 if (!_networking) DoCommand(0, critical ? PM_PAUSED_ERROR : PM_PAUSED_NORMAL, 1, DC_EXEC, CMD_PAUSE);
00229 }
00230
00231
00232 char buffer[512];
00233
00234 SetDParamStr(0, grfconfig->GetName());
00235 GetString(buffer, part1, lastof(buffer));
00236 DEBUG(grf, 0, "%s", buffer + 3);
00237
00238 SetDParam(1, engine);
00239 GetString(buffer, part2, lastof(buffer));
00240 DEBUG(grf, 0, "%s", buffer + 3);
00241 }
00242
00248 void VehicleLengthChanged(const Vehicle *u)
00249 {
00250
00251 const Engine *engine = u->GetEngine();
00252 uint32 grfid = engine->grf_prop.grffile->grfid;
00253 GRFConfig *grfconfig = GetGRFConfig(grfid);
00254 if (GamelogGRFBugReverse(grfid, engine->grf_prop.local_id) || !HasBit(grfconfig->grf_bugs, GBUG_VEH_LENGTH)) {
00255 ShowNewGrfVehicleError(u->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_VEHICLE_LENGTH, GBUG_VEH_LENGTH, true);
00256 }
00257 }
00258
00263 Vehicle::Vehicle(VehicleType type)
00264 {
00265 this->type = type;
00266 this->coord.left = INVALID_COORD;
00267 this->group_id = DEFAULT_GROUP;
00268 this->fill_percent_te_id = INVALID_TE_ID;
00269 this->first = this;
00270 this->colourmap = PAL_NONE;
00271 this->cargo_age_counter = 1;
00272 }
00273
00278 byte VehicleRandomBits()
00279 {
00280 return GB(Random(), 0, 8);
00281 }
00282
00283
00284
00285 const int HASH_BITS = 7;
00286 const int HASH_SIZE = 1 << HASH_BITS;
00287 const int HASH_MASK = HASH_SIZE - 1;
00288 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
00289 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
00290
00291
00292
00293 const int HASH_RES = 0;
00294
00295 static Vehicle *_vehicle_tile_hash[TOTAL_HASH_SIZE];
00296
00297 static Vehicle *VehicleFromTileHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
00298 {
00299 for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
00300 for (int x = xl; ; x = (x + 1) & HASH_MASK) {
00301 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
00302 for (; v != NULL; v = v->hash_tile_next) {
00303 Vehicle *a = proc(v, data);
00304 if (find_first && a != NULL) return a;
00305 }
00306 if (x == xu) break;
00307 }
00308 if (y == yu) break;
00309 }
00310
00311 return NULL;
00312 }
00313
00314
00326 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
00327 {
00328 const int COLL_DIST = 6;
00329
00330
00331 int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00332 int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00333 int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00334 int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00335
00336 return VehicleFromTileHash(xl, yl, xu, yu, data, proc, find_first);
00337 }
00338
00353 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00354 {
00355 VehicleFromPosXY(x, y, data, proc, false);
00356 }
00357
00369 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00370 {
00371 return VehicleFromPosXY(x, y, data, proc, true) != NULL;
00372 }
00373
00384 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
00385 {
00386 int x = GB(TileX(tile), HASH_RES, HASH_BITS);
00387 int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
00388
00389 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
00390 for (; v != NULL; v = v->hash_tile_next) {
00391 if (v->tile != tile) continue;
00392
00393 Vehicle *a = proc(v, data);
00394 if (find_first && a != NULL) return a;
00395 }
00396
00397 return NULL;
00398 }
00399
00413 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00414 {
00415 VehicleFromPos(tile, data, proc, false);
00416 }
00417
00428 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00429 {
00430 return VehicleFromPos(tile, data, proc, true) != NULL;
00431 }
00432
00439 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
00440 {
00441 int z = *(int*)data;
00442
00443 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
00444 if (v->z_pos > z) return NULL;
00445
00446 return v;
00447 }
00448
00454 CommandCost EnsureNoVehicleOnGround(TileIndex tile)
00455 {
00456 int z = GetTileMaxPixelZ(tile);
00457
00458
00459
00460
00461
00462 Vehicle *v = VehicleFromPos(tile, &z, &EnsureNoVehicleProcZ, true);
00463 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00464 return CommandCost();
00465 }
00466
00468 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
00469 {
00470 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
00471 if (v == (const Vehicle *)data) return NULL;
00472
00473 return v;
00474 }
00475
00483 CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
00484 {
00485
00486
00487
00488
00489 Vehicle *v = VehicleFromPos(tile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
00490 if (v == NULL) v = VehicleFromPos(endtile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
00491
00492 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00493 return CommandCost();
00494 }
00495
00496 static Vehicle *EnsureNoTrainOnTrackProc(Vehicle *v, void *data)
00497 {
00498 TrackBits rail_bits = *(TrackBits *)data;
00499
00500 if (v->type != VEH_TRAIN) return NULL;
00501
00502 Train *t = Train::From(v);
00503 if ((t->track != rail_bits) && !TracksOverlap(t->track | rail_bits)) return NULL;
00504
00505 return v;
00506 }
00507
00516 CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
00517 {
00518
00519
00520
00521
00522 Vehicle *v = VehicleFromPos(tile, &track_bits, &EnsureNoTrainOnTrackProc, true);
00523 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00524 return CommandCost();
00525 }
00526
00527 static void UpdateVehicleTileHash(Vehicle *v, bool remove)
00528 {
00529 Vehicle **old_hash = v->hash_tile_current;
00530 Vehicle **new_hash;
00531
00532 if (remove) {
00533 new_hash = NULL;
00534 } else {
00535 int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
00536 int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
00537 new_hash = &_vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
00538 }
00539
00540 if (old_hash == new_hash) return;
00541
00542
00543 if (old_hash != NULL) {
00544 if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = v->hash_tile_prev;
00545 *v->hash_tile_prev = v->hash_tile_next;
00546 }
00547
00548
00549 if (new_hash != NULL) {
00550 v->hash_tile_next = *new_hash;
00551 if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = &v->hash_tile_next;
00552 v->hash_tile_prev = new_hash;
00553 *new_hash = v;
00554 }
00555
00556
00557 v->hash_tile_current = new_hash;
00558 }
00559
00560 static Vehicle *_vehicle_viewport_hash[0x1000];
00561
00562 static void UpdateVehicleViewportHash(Vehicle *v, int x, int y)
00563 {
00564 Vehicle **old_hash, **new_hash;
00565 int old_x = v->coord.left;
00566 int old_y = v->coord.top;
00567
00568 new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(x, y)];
00569 old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(old_x, old_y)];
00570
00571 if (old_hash == new_hash) return;
00572
00573
00574 if (old_hash != NULL) {
00575 if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = v->hash_viewport_prev;
00576 *v->hash_viewport_prev = v->hash_viewport_next;
00577 }
00578
00579
00580 if (new_hash != NULL) {
00581 v->hash_viewport_next = *new_hash;
00582 if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = &v->hash_viewport_next;
00583 v->hash_viewport_prev = new_hash;
00584 *new_hash = v;
00585 }
00586 }
00587
00588 void ResetVehicleHash()
00589 {
00590 Vehicle *v;
00591 FOR_ALL_VEHICLES(v) { v->hash_tile_current = NULL; }
00592 memset(_vehicle_viewport_hash, 0, sizeof(_vehicle_viewport_hash));
00593 memset(_vehicle_tile_hash, 0, sizeof(_vehicle_tile_hash));
00594 }
00595
00596 void ResetVehicleColourMap()
00597 {
00598 Vehicle *v;
00599 FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
00600 }
00601
00606 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
00607 static AutoreplaceMap _vehicles_to_autoreplace;
00608
00609 void InitializeVehicles()
00610 {
00611 _vehicles_to_autoreplace.Reset();
00612 ResetVehicleHash();
00613 }
00614
00615 uint CountVehiclesInChain(const Vehicle *v)
00616 {
00617 uint count = 0;
00618 do count++; while ((v = v->Next()) != NULL);
00619 return count;
00620 }
00621
00626 bool Vehicle::IsEngineCountable() const
00627 {
00628 switch (this->type) {
00629 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
00630 case VEH_TRAIN:
00631 return !this->IsArticulatedPart() &&
00632 !Train::From(this)->IsRearDualheaded();
00633 case VEH_ROAD: return RoadVehicle::From(this)->IsFrontEngine();
00634 case VEH_SHIP: return true;
00635 default: return false;
00636 }
00637 }
00638
00643 bool Vehicle::HasEngineType() const
00644 {
00645 switch (this->type) {
00646 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
00647 case VEH_TRAIN:
00648 case VEH_ROAD:
00649 case VEH_SHIP: return true;
00650 default: return false;
00651 }
00652 }
00653
00659 const Engine *Vehicle::GetEngine() const
00660 {
00661 return Engine::Get(this->engine_type);
00662 }
00663
00669 const GRFFile *Vehicle::GetGRF() const
00670 {
00671 return this->GetEngine()->GetGRF();
00672 }
00673
00679 uint32 Vehicle::GetGRFID() const
00680 {
00681 return this->GetEngine()->GetGRFID();
00682 }
00683
00691 void Vehicle::HandlePathfindingResult(bool path_found)
00692 {
00693 if (path_found) {
00694
00695 if (!HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00696
00697
00698 ClrBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00699
00700 DeleteVehicleNews(this->index, STR_NEWS_VEHICLE_IS_LOST);
00701 return;
00702 }
00703
00704
00705 if (HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00706
00707
00708 SetBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00709
00710 AI::NewEvent(this->owner, new ScriptEventVehicleLost(this->index));
00711 if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) {
00712 SetDParam(0, this->index);
00713 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_LOST, this->index);
00714 }
00715 }
00716
00718 void Vehicle::PreDestructor()
00719 {
00720 if (CleaningPool()) return;
00721
00722 if (Station::IsValidID(this->last_station_visited)) {
00723 Station *st = Station::Get(this->last_station_visited);
00724 st->loading_vehicles.remove(this);
00725
00726 HideFillingPercent(&this->fill_percent_te_id);
00727 this->CancelReservation(INVALID_STATION, st);
00728 delete this->cargo_payment;
00729 }
00730
00731 if (this->IsEngineCountable()) {
00732 GroupStatistics::CountEngine(this, -1);
00733 if (this->IsPrimaryVehicle()) GroupStatistics::CountVehicle(this, -1);
00734 GroupStatistics::UpdateAutoreplace(this->owner);
00735
00736 if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
00737 DeleteGroupHighlightOfVehicle(this);
00738 }
00739
00740 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
00741 Aircraft *a = Aircraft::From(this);
00742 Station *st = GetTargetAirportIfValid(a);
00743 if (st != NULL) {
00744 const AirportFTA *layout = st->airport.GetFTA()->layout;
00745 CLRBITS(st->airport.flags, layout[a->previous_pos].block | layout[a->pos].block);
00746 }
00747 }
00748
00749
00750 if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
00751 RoadVehicle *v = RoadVehicle::From(this);
00752 if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
00753
00754 RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
00755 }
00756 }
00757
00758 if (this->Previous() == NULL) {
00759 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
00760 }
00761
00762 if (this->IsPrimaryVehicle()) {
00763 DeleteWindowById(WC_VEHICLE_VIEW, this->index);
00764 DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
00765 DeleteWindowById(WC_VEHICLE_REFIT, this->index);
00766 DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
00767 DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
00768 SetWindowDirty(WC_COMPANY, this->owner);
00769 OrderBackup::ClearVehicle(this);
00770 }
00771 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00772
00773 this->cargo.Truncate(0);
00774 DeleteVehicleOrders(this);
00775 DeleteDepotHighlightOfVehicle(this);
00776
00777 extern void StopGlobalFollowVehicle(const Vehicle *v);
00778 StopGlobalFollowVehicle(this);
00779
00780 ReleaseDisastersTargetingVehicle(this->index);
00781 }
00782
00783 Vehicle::~Vehicle()
00784 {
00785 if (CleaningPool()) {
00786 this->cargo.OnCleanPool();
00787 return;
00788 }
00789
00790
00791
00792 if (!(this->vehstatus & VS_HIDDEN)) MarkSingleVehicleDirty(this);
00793
00794 Vehicle *v = this->Next();
00795 this->SetNext(NULL);
00796
00797 delete v;
00798
00799 UpdateVehicleTileHash(this, true);
00800 UpdateVehicleViewportHash(this, INVALID_COORD, 0);
00801 DeleteVehicleNews(this->index, INVALID_STRING_ID);
00802 DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index);
00803 }
00804
00809 void VehicleEnteredDepotThisTick(Vehicle *v)
00810 {
00811
00812 _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
00813
00814
00815
00816
00817
00818
00819 v->vehstatus |= VS_STOPPED;
00820 }
00821
00827 static void RunVehicleDayProc()
00828 {
00829 if (_game_mode != GM_NORMAL) return;
00830
00831
00832 for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
00833 Vehicle *v = Vehicle::Get(i);
00834 if (v == NULL) continue;
00835
00836
00837 if ((v->day_counter & 0x1F) == 0 && v->HasEngineType()) {
00838 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
00839 if (callback != CALLBACK_FAILED) {
00840 if (HasBit(callback, 0)) TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32);
00841 if (HasBit(callback, 1)) v->colourmap = PAL_NONE;
00842
00843 if (callback & ~3) ErrorUnknownCallbackResult(v->GetGRFID(), CBID_VEHICLE_32DAY_CALLBACK, callback);
00844 }
00845 }
00846
00847
00848 v->OnNewDay();
00849 }
00850 }
00851
00852 void CallVehicleTicks()
00853 {
00854 _vehicles_to_autoreplace.Clear();
00855
00856 RunVehicleDayProc();
00857
00858 Station *st;
00859 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
00860
00861 Vehicle *v;
00862 FOR_ALL_VEHICLES(v) {
00863
00864 if (!v->Tick()) {
00865 assert(Vehicle::Get(vehicle_index) == NULL);
00866 continue;
00867 }
00868
00869 assert(Vehicle::Get(vehicle_index) == v);
00870
00871 switch (v->type) {
00872 default: break;
00873
00874 case VEH_TRAIN:
00875 case VEH_ROAD:
00876 case VEH_AIRCRAFT:
00877 case VEH_SHIP:
00878 if (v->vcache.cached_cargo_age_period != 0) {
00879 v->cargo_age_counter = min(v->cargo_age_counter, v->vcache.cached_cargo_age_period);
00880 if (--v->cargo_age_counter == 0) {
00881 v->cargo.AgeCargo();
00882 v->cargo_age_counter = v->vcache.cached_cargo_age_period;
00883 }
00884 }
00885
00886 if (v->type == VEH_TRAIN && Train::From(v)->IsWagon()) continue;
00887 if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
00888 if (v->type == VEH_ROAD && !RoadVehicle::From(v)->IsFrontEngine()) continue;
00889
00890 v->motion_counter += v->cur_speed;
00891
00892 if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
00893
00894
00895 if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
00896 }
00897 }
00898
00899 Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
00900 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
00901 v = it->first;
00902
00903 cur_company.Change(v->owner);
00904
00905
00906
00907
00908 if (it->second) v->vehstatus &= ~VS_STOPPED;
00909
00910
00911 int x = v->x_pos;
00912 int y = v->y_pos;
00913 int z = v->z_pos;
00914
00915 const Company *c = Company::Get(_current_company);
00916 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
00917 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
00918 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
00919
00920 if (!IsLocalCompany()) continue;
00921
00922 if (res.Succeeded()) {
00923 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
00924 continue;
00925 }
00926
00927 StringID error_message = res.GetErrorMessage();
00928 if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
00929
00930 if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
00931
00932 StringID message;
00933 if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
00934 message = error_message;
00935 } else {
00936 message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
00937 }
00938
00939 SetDParam(0, v->index);
00940 SetDParam(1, error_message);
00941 AddVehicleAdviceNewsItem(message, v->index);
00942 }
00943
00944 cur_company.Restore();
00945 }
00946
00951 static void DoDrawVehicle(const Vehicle *v)
00952 {
00953 SpriteID image = v->cur_image;
00954 PaletteID pal = PAL_NONE;
00955
00956 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
00957
00958
00959 bool shadowed = (v->vehstatus & VS_SHADOW) != 0;
00960
00961 if (v->type == VEH_EFFECT) {
00962
00963
00964 TransparencyOption to = EffectVehicle::From(v)->GetTransparencyOption();
00965 if (to != TO_INVALID && (IsTransparencySet(to) || IsInvisibilitySet(to))) return;
00966 }
00967
00968 AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
00969 v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs);
00970 }
00971
00976 void ViewportAddVehicles(DrawPixelInfo *dpi)
00977 {
00978
00979 const int l = dpi->left;
00980 const int r = dpi->left + dpi->width;
00981 const int t = dpi->top;
00982 const int b = dpi->top + dpi->height;
00983
00984
00985 int xl, xu, yl, yu;
00986
00987 if (dpi->width + (70 * ZOOM_LVL_BASE) < (1 << (7 + 6 + ZOOM_LVL_SHIFT))) {
00988 xl = GB(l - (70 * ZOOM_LVL_BASE), 7 + ZOOM_LVL_SHIFT, 6);
00989 xu = GB(r, 7 + ZOOM_LVL_SHIFT, 6);
00990 } else {
00991
00992 xl = 0;
00993 xu = 0x3F;
00994 }
00995
00996 if (dpi->height + (70 * ZOOM_LVL_BASE) < (1 << (6 + 6 + ZOOM_LVL_SHIFT))) {
00997 yl = GB(t - (70 * ZOOM_LVL_BASE), 6 + ZOOM_LVL_SHIFT, 6) << 6;
00998 yu = GB(b, 6 + ZOOM_LVL_SHIFT, 6) << 6;
00999 } else {
01000
01001 yl = 0;
01002 yu = 0x3F << 6;
01003 }
01004
01005 for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
01006 for (int x = xl;; x = (x + 1) & 0x3F) {
01007 const Vehicle *v = _vehicle_viewport_hash[x + y];
01008
01009 while (v != NULL) {
01010 if (!(v->vehstatus & VS_HIDDEN) &&
01011 l <= v->coord.right &&
01012 t <= v->coord.bottom &&
01013 r >= v->coord.left &&
01014 b >= v->coord.top) {
01015 DoDrawVehicle(v);
01016 }
01017 v = v->hash_viewport_next;
01018 }
01019
01020 if (x == xu) break;
01021 }
01022
01023 if (y == yu) break;
01024 }
01025 }
01026
01034 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
01035 {
01036 Vehicle *found = NULL, *v;
01037 uint dist, best_dist = UINT_MAX;
01038
01039 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
01040
01041 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
01042 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
01043
01044 FOR_ALL_VEHICLES(v) {
01045 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
01046 x >= v->coord.left && x <= v->coord.right &&
01047 y >= v->coord.top && y <= v->coord.bottom) {
01048
01049 dist = max(
01050 abs(((v->coord.left + v->coord.right) >> 1) - x),
01051 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
01052 );
01053
01054 if (dist < best_dist) {
01055 found = v;
01056 best_dist = dist;
01057 }
01058 }
01059 }
01060
01061 return found;
01062 }
01063
01068 void DecreaseVehicleValue(Vehicle *v)
01069 {
01070 v->value -= v->value >> 8;
01071 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01072 }
01073
01074 static const byte _breakdown_chance[64] = {
01075 3, 3, 3, 3, 3, 3, 3, 3,
01076 4, 4, 5, 5, 6, 6, 7, 7,
01077 8, 8, 9, 9, 10, 10, 11, 11,
01078 12, 13, 13, 13, 13, 14, 15, 16,
01079 17, 19, 21, 25, 28, 31, 34, 37,
01080 40, 44, 48, 52, 56, 60, 64, 68,
01081 72, 80, 90, 100, 110, 120, 130, 140,
01082 150, 170, 190, 210, 230, 250, 250, 250,
01083 };
01084
01085 void CheckVehicleBreakdown(Vehicle *v)
01086 {
01087 int rel, rel_old;
01088
01089
01090 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
01091 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01092
01093 if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
01094 _settings_game.difficulty.vehicle_breakdowns < 1 ||
01095 v->cur_speed < 5 || _game_mode == GM_MENU) {
01096 return;
01097 }
01098
01099 uint32 r = Random();
01100
01101
01102 int chance = v->breakdown_chance + 1;
01103 if (Chance16I(1, 25, r)) chance += 25;
01104 v->breakdown_chance = min(255, chance);
01105
01106
01107 rel = v->reliability;
01108 if (v->type == VEH_SHIP) rel += 0x6666;
01109
01110
01111 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
01112
01113
01114 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
01115 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
01116 v->breakdown_delay = GB(r, 24, 7) + 0x80;
01117 v->breakdown_chance = 0;
01118 }
01119 }
01120
01127 bool Vehicle::HandleBreakdown()
01128 {
01129
01130
01131
01132
01133
01134 switch (this->breakdown_ctr) {
01135 case 0:
01136 return false;
01137
01138 case 2:
01139 this->breakdown_ctr = 1;
01140
01141 if (this->breakdowns_since_last_service != 255) {
01142 this->breakdowns_since_last_service++;
01143 }
01144
01145 if (this->type == VEH_AIRCRAFT) {
01146
01147 this->vehstatus |= VS_AIRCRAFT_BROKEN;
01148 } else {
01149 this->cur_speed = 0;
01150
01151 if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
01152 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
01153 (this->type == VEH_TRAIN ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
01154 (this->type == VEH_TRAIN ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
01155 }
01156
01157 if (!(this->vehstatus & VS_HIDDEN) && !HasBit(EngInfo(this->engine_type)->misc_flags, EF_NO_BREAKDOWN_SMOKE)) {
01158 EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
01159 if (u != NULL) u->animation_state = this->breakdown_delay * 2;
01160 }
01161 }
01162
01163 this->MarkDirty();
01164 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01165 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01166
01167
01168 case 1:
01169
01170 if (this->type == VEH_AIRCRAFT) return false;
01171
01172
01173 if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
01174 if (--this->breakdown_delay == 0) {
01175 this->breakdown_ctr = 0;
01176 this->MarkDirty();
01177 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01178 }
01179 }
01180 return true;
01181
01182 default:
01183 if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
01184 return false;
01185 }
01186 }
01187
01192 void AgeVehicle(Vehicle *v)
01193 {
01194 if (v->age < MAX_DAY) {
01195 v->age++;
01196 if (v->IsPrimaryVehicle() && v->age == VEHICLE_PROFIT_MIN_AGE + 1) GroupStatistics::VehicleReachedProfitAge(v);
01197 }
01198
01199 if (!v->IsPrimaryVehicle() && (v->type != VEH_TRAIN || !Train::From(v)->IsEngine())) return;
01200
01201 int age = v->age - v->max_age;
01202 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
01203 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
01204 v->reliability_spd_dec <<= 1;
01205 }
01206
01207 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01208
01209
01210 if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
01211
01212
01213 if (Company::Get(v->owner)->settings.engine_renew && v->GetEngine()->company_avail != 0) return;
01214
01215 StringID str;
01216 if (age == -DAYS_IN_LEAP_YEAR) {
01217 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
01218 } else if (age == 0) {
01219 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
01220 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
01221 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
01222 } else {
01223 return;
01224 }
01225
01226 SetDParam(0, v->index);
01227 AddVehicleAdviceNewsItem(str, v->index);
01228 }
01229
01236 uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *colour)
01237 {
01238 int count = 0;
01239 int max = 0;
01240 int cars = 0;
01241 int unloading = 0;
01242 bool loading = false;
01243
01244 const Vehicle *u = v;
01245
01246 const Station *st = Station::GetIfValid(v->last_station_visited);
01247 assert(colour == NULL || st != NULL);
01248
01249
01250 for (; v != NULL; v = v->Next()) {
01251 count += v->cargo.OnboardCount();
01252 max += v->cargo_cap;
01253 if (v->cargo_cap != 0 && colour != NULL) {
01254 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
01255 loading |= !(u->current_order.GetLoadType() & OLFB_NO_LOAD) && st->goods[v->cargo_type].days_since_pickup != 255;
01256 cars++;
01257 }
01258 }
01259
01260 if (colour != NULL) {
01261 if (unloading == 0 && loading) {
01262 *colour = STR_PERCENT_UP;
01263 } else if (unloading == 0 && !loading) {
01264 *colour = STR_PERCENT_NONE;
01265 } else if (cars == unloading || !loading) {
01266 *colour = STR_PERCENT_DOWN;
01267 } else {
01268 *colour = STR_PERCENT_UP_DOWN;
01269 }
01270 }
01271
01272
01273 if (max == 0) return 100;
01274
01275
01276 return (count * 100) / max;
01277 }
01278
01283 void VehicleEnterDepot(Vehicle *v)
01284 {
01285
01286 assert(v == v->First());
01287
01288 switch (v->type) {
01289 case VEH_TRAIN: {
01290 Train *t = Train::From(v);
01291 SetWindowClassesDirty(WC_TRAINS_LIST);
01292
01293 SetDepotReservation(t->tile, false);
01294 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
01295
01296 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
01297 t->wait_counter = 0;
01298 t->force_proceed = TFP_NONE;
01299 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
01300 t->ConsistChanged(true);
01301 break;
01302 }
01303
01304 case VEH_ROAD:
01305 SetWindowClassesDirty(WC_ROADVEH_LIST);
01306 break;
01307
01308 case VEH_SHIP: {
01309 SetWindowClassesDirty(WC_SHIPS_LIST);
01310 Ship *ship = Ship::From(v);
01311 ship->state = TRACK_BIT_DEPOT;
01312 ship->UpdateCache();
01313 ship->UpdateViewport(true, true);
01314 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01315 break;
01316 }
01317
01318 case VEH_AIRCRAFT:
01319 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01320 HandleAircraftEnterHangar(Aircraft::From(v));
01321 break;
01322 default: NOT_REACHED();
01323 }
01324 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01325
01326 if (v->type != VEH_TRAIN) {
01327
01328
01329 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01330 }
01331 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01332
01333 v->vehstatus |= VS_HIDDEN;
01334 v->cur_speed = 0;
01335
01336 VehicleServiceInDepot(v);
01337
01338 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01339
01340 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01341 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01342
01343 const Order *real_order = v->GetOrder(v->cur_real_order_index);
01344 Order t = v->current_order;
01345 v->current_order.MakeDummy();
01346
01347
01348
01349 if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
01350 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
01351 (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
01352
01353 return;
01354 }
01355
01356 if (t.IsRefit()) {
01357 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01358 CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
01359 cur_company.Restore();
01360
01361 if (cost.Failed()) {
01362 _vehicles_to_autoreplace[v] = false;
01363 if (v->owner == _local_company) {
01364
01365 SetDParam(0, v->index);
01366 AddVehicleAdviceNewsItem(STR_NEWS_ORDER_REFIT_FAILED, v->index);
01367 }
01368 } else if (cost.GetCost() != 0) {
01369 v->profit_this_year -= cost.GetCost() << 8;
01370 if (v->owner == _local_company) {
01371 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01372 }
01373 }
01374 }
01375
01376 if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01377
01378 v->DeleteUnreachedImplicitOrders();
01379 UpdateVehicleTimetable(v, true);
01380 v->IncrementImplicitOrderIndex();
01381 }
01382 if (t.GetDepotActionType() & ODATFB_HALT) {
01383
01384 _vehicles_to_autoreplace[v] = false;
01385 if (v->owner == _local_company) {
01386 SetDParam(0, v->index);
01387 AddVehicleAdviceNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, v->index);
01388 }
01389 AI::NewEvent(v->owner, new ScriptEventVehicleWaitingInDepot(v->index));
01390 }
01391 }
01392 }
01393
01394
01400 void VehicleUpdatePosition(Vehicle *v)
01401 {
01402 UpdateVehicleTileHash(v, false);
01403 }
01404
01411 void VehicleUpdateViewport(Vehicle *v, bool dirty)
01412 {
01413 int img = v->cur_image;
01414 Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
01415 const Sprite *spr = GetSprite(img, ST_NORMAL);
01416
01417 pt.x += spr->x_offs;
01418 pt.y += spr->y_offs;
01419
01420 UpdateVehicleViewportHash(v, pt.x, pt.y);
01421
01422 Rect old_coord = v->coord;
01423 v->coord.left = pt.x;
01424 v->coord.top = pt.y;
01425 v->coord.right = pt.x + spr->width + 2 * ZOOM_LVL_BASE;
01426 v->coord.bottom = pt.y + spr->height + 2 * ZOOM_LVL_BASE;
01427
01428 if (dirty) {
01429 if (old_coord.left == INVALID_COORD) {
01430 MarkSingleVehicleDirty(v);
01431 } else {
01432 MarkAllViewportsDirty(
01433 min(old_coord.left, v->coord.left),
01434 min(old_coord.top, v->coord.top),
01435 max(old_coord.right, v->coord.right) + 1 * ZOOM_LVL_BASE,
01436 max(old_coord.bottom, v->coord.bottom) + 1 * ZOOM_LVL_BASE
01437 );
01438 }
01439 }
01440 }
01441
01446 void VehicleUpdatePositionAndViewport(Vehicle *v)
01447 {
01448 VehicleUpdatePosition(v);
01449 VehicleUpdateViewport(v, true);
01450 }
01451
01456 void MarkSingleVehicleDirty(const Vehicle *v)
01457 {
01458 MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1 * ZOOM_LVL_BASE, v->coord.bottom + 1 * ZOOM_LVL_BASE);
01459 }
01460
01466 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01467 {
01468 static const int8 _delta_coord[16] = {
01469 -1,-1,-1, 0, 1, 1, 1, 0,
01470 -1, 0, 1, 1, 1, 0,-1,-1,
01471 };
01472
01473 int x = v->x_pos + _delta_coord[v->direction];
01474 int y = v->y_pos + _delta_coord[v->direction + 8];
01475
01476 GetNewVehiclePosResult gp;
01477 gp.x = x;
01478 gp.y = y;
01479 gp.old_tile = v->tile;
01480 gp.new_tile = TileVirtXY(x, y);
01481 return gp;
01482 }
01483
01484 static const Direction _new_direction_table[] = {
01485 DIR_N, DIR_NW, DIR_W,
01486 DIR_NE, DIR_SE, DIR_SW,
01487 DIR_E, DIR_SE, DIR_S
01488 };
01489
01490 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01491 {
01492 int i = 0;
01493
01494 if (y >= v->y_pos) {
01495 if (y != v->y_pos) i += 3;
01496 i += 3;
01497 }
01498
01499 if (x >= v->x_pos) {
01500 if (x != v->x_pos) i++;
01501 i++;
01502 }
01503
01504 Direction dir = v->direction;
01505
01506 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01507 if (dirdiff == DIRDIFF_SAME) return dir;
01508 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01509 }
01510
01520 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01521 {
01522 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01523 }
01524
01532 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01533 {
01534
01535 const Vehicle *v;
01536 FOR_ALL_VEHICLES(v) {
01537 if (v->type == type && v->owner == owner) {
01538 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01539 }
01540 }
01541
01542 if (this->maxid == 0) return;
01543
01544
01545
01546
01547 this->cache = CallocT<bool>(this->maxid + 2);
01548
01549
01550 FOR_ALL_VEHICLES(v) {
01551 if (v->type == type && v->owner == owner) {
01552 this->cache[v->unitnumber] = true;
01553 }
01554 }
01555 }
01556
01558 UnitID FreeUnitIDGenerator::NextID()
01559 {
01560 if (this->maxid <= this->curid) return ++this->curid;
01561
01562 while (this->cache[++this->curid]) { }
01563
01564 return this->curid;
01565 }
01566
01572 UnitID GetFreeUnitNumber(VehicleType type)
01573 {
01574
01575 uint max_veh;
01576 switch (type) {
01577 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
01578 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
01579 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
01580 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
01581 default: NOT_REACHED();
01582 }
01583
01584 const Company *c = Company::Get(_current_company);
01585 if (c->group_all[type].num_vehicle >= max_veh) return UINT16_MAX;
01586
01587 FreeUnitIDGenerator gen(type, _current_company);
01588
01589 return gen.NextID();
01590 }
01591
01592
01601 bool CanBuildVehicleInfrastructure(VehicleType type)
01602 {
01603 assert(IsCompanyBuildableVehicleType(type));
01604
01605 if (!Company::IsValidID(_local_company)) return false;
01606 if (!_settings_client.gui.disable_unsuitable_building) return true;
01607
01608 UnitID max;
01609 switch (type) {
01610 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
01611 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
01612 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
01613 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01614 default: NOT_REACHED();
01615 }
01616
01617
01618 if (max > 0) {
01619
01620 const Engine *e;
01621 FOR_ALL_ENGINES_OF_TYPE(e, type) {
01622 if (HasBit(e->company_avail, _local_company)) return true;
01623 }
01624 return false;
01625 }
01626
01627
01628 const Vehicle *v;
01629 FOR_ALL_VEHICLES(v) {
01630 if (v->owner == _local_company && v->type == type) return true;
01631 }
01632
01633 return false;
01634 }
01635
01636
01644 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
01645 {
01646 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01647 const Engine *e = Engine::Get(engine_type);
01648 switch (e->type) {
01649 default: NOT_REACHED();
01650 case VEH_TRAIN:
01651 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
01652
01653
01654 engine_type = parent_engine_type;
01655 e = Engine::Get(engine_type);
01656
01657 }
01658
01659 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01660 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01661 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
01662 if (!CargoSpec::Get(cargo_type)->is_freight) {
01663 if (parent_engine_type == INVALID_ENGINE) {
01664 return LS_PASSENGER_WAGON_STEAM;
01665 } else {
01666 switch (RailVehInfo(parent_engine_type)->engclass) {
01667 default: NOT_REACHED();
01668 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
01669 case EC_DIESEL: return LS_PASSENGER_WAGON_DIESEL;
01670 case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
01671 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
01672 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
01673 }
01674 }
01675 } else {
01676 return LS_FREIGHT_WAGON;
01677 }
01678 } else {
01679 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
01680
01681 switch (e->u.rail.engclass) {
01682 default: NOT_REACHED();
01683 case EC_STEAM: return LS_STEAM;
01684 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
01685 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
01686 case EC_MONORAIL: return LS_MONORAIL;
01687 case EC_MAGLEV: return LS_MAGLEV;
01688 }
01689 }
01690
01691 case VEH_ROAD:
01692
01693 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01694 engine_type = parent_engine_type;
01695 e = Engine::Get(engine_type);
01696 cargo_type = v->First()->cargo_type;
01697 }
01698 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01699 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01700
01701
01702 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
01703
01704 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01705 } else {
01706
01707 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01708 }
01709
01710 case VEH_SHIP:
01711 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01712 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01713 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01714
01715 case VEH_AIRCRAFT:
01716 switch (e->u.air.subtype) {
01717 case AIR_HELI: return LS_HELICOPTER;
01718 case AIR_CTOL: return LS_SMALL_PLANE;
01719 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
01720 default: NOT_REACHED();
01721 }
01722 }
01723 }
01724
01734 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
01735 {
01736 const Company *c = Company::Get(company);
01737 LiveryScheme scheme = LS_DEFAULT;
01738
01739
01740
01741 if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
01742
01743 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
01744
01745
01746 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01747 }
01748
01749 return &c->livery[scheme];
01750 }
01751
01752
01753 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01754 {
01755 PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01756
01757
01758 if (map != PAL_NONE) return map;
01759
01760 const Engine *e = Engine::Get(engine_type);
01761
01762
01763 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
01764 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01765
01766 if (callback != CALLBACK_FAILED) {
01767 assert_compile(PAL_NONE == 0);
01768 map = GB(callback, 0, 14);
01769
01770
01771 if (!HasBit(callback, 14)) {
01772
01773 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01774 return map;
01775 }
01776 }
01777 }
01778
01779 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
01780
01781 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
01782
01783
01784 if (!Company::IsValidID(company)) return map;
01785
01786 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
01787
01788 map += livery->colour1;
01789 if (twocc) map += livery->colour2 * 16;
01790
01791
01792 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01793 return map;
01794 }
01795
01802 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
01803 {
01804 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01805 }
01806
01812 PaletteID GetVehiclePalette(const Vehicle *v)
01813 {
01814 if (v->IsGroundVehicle()) {
01815 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
01816 }
01817
01818 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01819 }
01820
01824 void Vehicle::DeleteUnreachedImplicitOrders()
01825 {
01826 if (this->IsGroundVehicle()) {
01827 uint16 &gv_flags = this->GetGroundVehicleFlags();
01828 if (HasBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)) {
01829
01830 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
01831 this->cur_implicit_order_index = this->cur_real_order_index;
01832 InvalidateVehicleOrder(this, 0);
01833 return;
01834 }
01835 }
01836
01837 const Order *order = this->GetOrder(this->cur_implicit_order_index);
01838 while (order != NULL) {
01839 if (this->cur_implicit_order_index == this->cur_real_order_index) break;
01840
01841 if (order->IsType(OT_IMPLICIT)) {
01842
01843 order = order->next;
01844 DeleteOrder(this, this->cur_implicit_order_index);
01845 } else {
01846
01847 order = order->next;
01848 this->cur_implicit_order_index++;
01849 }
01850
01851
01852 if (order == NULL) {
01853 order = this->GetOrder(0);
01854 this->cur_implicit_order_index = 0;
01855 }
01856 }
01857 }
01858
01863 void Vehicle::BeginLoading()
01864 {
01865 assert(IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP);
01866
01867 if (this->current_order.IsType(OT_GOTO_STATION) &&
01868 this->current_order.GetDestination() == this->last_station_visited) {
01869 this->DeleteUnreachedImplicitOrders();
01870
01871
01872 this->current_order.MakeLoading(true);
01873 UpdateVehicleTimetable(this, true);
01874
01875
01876
01877
01878
01879
01880 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
01881
01882 } else {
01883
01884
01885
01886
01887
01888 Order *in_list = this->GetOrder(this->cur_implicit_order_index);
01889 if (this->IsGroundVehicle() && in_list != NULL &&
01890 (!in_list->IsType(OT_IMPLICIT) ||
01891 in_list->GetDestination() != this->last_station_visited)) {
01892 bool suppress_implicit_orders = HasBit(this->GetGroundVehicleFlags(), GVF_SUPPRESS_IMPLICIT_ORDERS);
01893
01894 Order *prev_order = this->cur_implicit_order_index > 0 ? this->GetOrder(this->cur_implicit_order_index - 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : NULL);
01895 if (prev_order == NULL ||
01896 (!prev_order->IsType(OT_IMPLICIT) && !prev_order->IsType(OT_GOTO_STATION)) ||
01897 prev_order->GetDestination() != this->last_station_visited) {
01898
01899
01900
01901 int target_index = this->cur_implicit_order_index;
01902 bool found = false;
01903 while (target_index != this->cur_real_order_index) {
01904 const Order *order = this->GetOrder(target_index);
01905 if (order->IsType(OT_IMPLICIT) && order->GetDestination() == this->last_station_visited) {
01906 found = true;
01907 break;
01908 }
01909 target_index++;
01910 if (target_index >= this->orders.list->GetNumOrders()) target_index = 0;
01911 assert(target_index != this->cur_implicit_order_index);
01912 }
01913
01914 if (found) {
01915 if (suppress_implicit_orders) {
01916
01917 this->cur_implicit_order_index = target_index;
01918 InvalidateVehicleOrder(this, 0);
01919 } else {
01920
01921 const Order *order = this->GetOrder(this->cur_implicit_order_index);
01922 while (!order->IsType(OT_IMPLICIT) || order->GetDestination() != this->last_station_visited) {
01923 if (order->IsType(OT_IMPLICIT)) {
01924
01925 order = order->next;
01926 DeleteOrder(this, this->cur_implicit_order_index);
01927 } else {
01928
01929 order = order->next;
01930 this->cur_implicit_order_index++;
01931 }
01932
01933
01934 if (order == NULL) {
01935 order = this->GetOrder(0);
01936 this->cur_implicit_order_index = 0;
01937 }
01938 assert(order != NULL);
01939 }
01940 }
01941 } else if (!suppress_implicit_orders && this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID && Order::CanAllocateItem()) {
01942
01943 Order *implicit_order = new Order();
01944 implicit_order->MakeImplicit(this->last_station_visited);
01945 InsertOrder(this, implicit_order, this->cur_implicit_order_index);
01946 if (this->cur_implicit_order_index > 0) --this->cur_implicit_order_index;
01947
01948
01949
01950 uint16 &gv_flags = this->GetGroundVehicleFlags();
01951 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
01952 }
01953 }
01954 }
01955 this->current_order.MakeLoading(false);
01956 }
01957
01958 if (this->last_loading_station != INVALID_STATION &&
01959 this->last_loading_station != this->last_station_visited &&
01960 ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
01961 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0)) {
01962 IncreaseStats(Station::Get(this->last_loading_station), this, this->last_station_visited);
01963 }
01964
01965 PrepareUnload(this);
01966
01967 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
01968 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
01969 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01970 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
01971
01972 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
01973 this->cur_speed = 0;
01974 this->MarkDirty();
01975 }
01976
01981 void Vehicle::CancelReservation(StationID next, Station *st)
01982 {
01983 for (Vehicle *v = this; v != NULL; v = v->next) {
01984 VehicleCargoList &cargo = v->cargo;
01985 if (cargo.ReservedCount() > 0) {
01986 DEBUG(misc, 1, "cancelling cargo reservation");
01987 GoodsEntry &ge = st->goods[v->cargo_type];
01988 cargo.Unreserve(next, &ge.cargo);
01989 SetBit(ge.acceptance_pickup, GoodsEntry::GES_PICKUP);
01990 }
01991 }
01992 }
01993
01998 void Vehicle::LeaveStation()
01999 {
02000 assert(this->current_order.IsType(OT_LOADING));
02001
02002 delete this->cargo_payment;
02003
02004
02005 if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
02006
02007 if ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
02008 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
02009 if (this->current_order.CanLeaveWithCargo(this->last_loading_station != INVALID_STATION)) {
02010
02011
02012
02013 this->RefreshNextHopsStats();
02014
02015
02016 this->last_loading_station = this->last_station_visited;
02017 } else {
02018
02019
02020 this->last_loading_station = INVALID_STATION;
02021 }
02022 }
02023
02024 this->current_order.MakeLeaveStation();
02025 Station *st = Station::Get(this->last_station_visited);
02026 this->CancelReservation(INVALID_STATION, st);
02027 st->loading_vehicles.remove(this);
02028
02029 HideFillingPercent(&this->fill_percent_te_id);
02030
02031 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
02032
02033 if (IsTileType(this->tile, MP_STATION)) TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
02034
02035 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
02036 }
02037 }
02038
02045 void Vehicle::RefreshNextHopsStats()
02046 {
02047
02048 SmallMap<CargoID, uint, 1> capacities;
02049 for (Vehicle *v = this; v != NULL; v = v->Next()) {
02050 v->refit_cap = v->cargo_cap;
02051 if (v->refit_cap == 0) continue;
02052 SmallPair<CargoID, uint> *i = capacities.Find(v->cargo_type);
02053 if (i == capacities.End()) {
02054
02055 i = capacities.Append();
02056 i->first = v->cargo_type;
02057 i->second = v->cargo_cap;
02058 } else {
02059 i->second += v->cargo_cap;
02060 }
02061 }
02062
02063
02064 if (this->orders.list == NULL) return;
02065
02066 uint hops = 0;
02067 const Order *first = this->orders.list->GetNextStoppingOrder(this,
02068 this->GetOrder(this->cur_implicit_order_index), hops);
02069 const Order *cur = first;
02070 const Order *next = first;
02071 while (next != NULL && cur->CanLeaveWithCargo(true)) {
02072 next = this->orders.list->GetNextStoppingOrder(this,
02073 this->orders.list->GetNext(next), ++hops);
02074 if (next == NULL) break;
02075
02076 if (next->IsType(OT_GOTO_DEPOT)) {
02077
02078 CargoID new_cid = next->GetRefitCargo();
02079 byte new_subtype = next->GetRefitSubtype();
02080 for (Vehicle *v = this; v != NULL; v = v->Next()) {
02081 const Engine *e = Engine::Get(v->engine_type);
02082 if (!HasBit(e->info.refit_mask, new_cid)) continue;
02083
02084
02085 CargoID temp_cid = v->cargo_type;
02086 byte temp_subtype = v->cargo_subtype;
02087 v->cargo_type = new_cid;
02088 v->cargo_subtype = new_subtype;
02089
02090 uint16 mail_capacity = 0;
02091 uint amount = e->DetermineCapacity(v, &mail_capacity);
02092
02093
02094 v->cargo_type = temp_cid;
02095 v->cargo_subtype = temp_subtype;
02096
02097
02098 if (new_cid != v->cargo_type && v->refit_cap > 0) {
02099 capacities[v->cargo_type] -= v->refit_cap;
02100 v->refit_cap = 0;
02101 } else if (amount < v->refit_cap) {
02102 capacities[v->cargo_type] -= v->refit_cap - amount;
02103 v->refit_cap = amount;
02104 }
02105
02106
02107 if (v->type == VEH_AIRCRAFT) {
02108 Vehicle *u = v->Next();
02109 if (mail_capacity < u->refit_cap) {
02110 capacities[u->cargo_type] -= u->refit_cap - mail_capacity;
02111 u->refit_cap = mail_capacity;
02112 }
02113 break;
02114 }
02115 if (v->type == VEH_SHIP) break;
02116 }
02117 } else {
02118 StationID next_station = next->GetDestination();
02119 Station *st = Station::GetIfValid(cur->GetDestination());
02120 if (st != NULL && next_station != INVALID_STATION && next_station != st->index) {
02121 for (const SmallPair<CargoID, uint> *i = capacities.Begin(); i != capacities.End(); ++i) {
02122
02123 if (i->second > 0) IncreaseStats(st, i->first, next_station, i->second, UINT_MAX);
02124 }
02125 }
02126 cur = next;
02127 if (cur == first) break;
02128 }
02129 }
02130
02131 for (Vehicle *v = this; v != NULL; v = v->Next()) v->refit_cap = v->cargo_cap;
02132 }
02133
02139 void Vehicle::HandleLoading(bool mode)
02140 {
02141 switch (this->current_order.GetType()) {
02142 case OT_LOADING: {
02143 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
02144
02145
02146 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
02147
02148 this->PlayLeaveStationSound();
02149
02150 this->LeaveStation();
02151
02152
02153 const Order *order = this->GetOrder(this->cur_implicit_order_index);
02154 if (order == NULL ||
02155 (!order->IsType(OT_IMPLICIT) && !order->IsType(OT_GOTO_STATION)) ||
02156 order->GetDestination() != this->last_station_visited) {
02157 return;
02158 }
02159 break;
02160 }
02161
02162 case OT_DUMMY: break;
02163
02164 default: return;
02165 }
02166
02167 this->IncrementImplicitOrderIndex();
02168 }
02169
02174 void Vehicle::GetConsistFreeCapacities(SmallMap<CargoID, uint> &capacities) const
02175 {
02176 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
02177 if (v->cargo_cap == 0) continue;
02178 SmallPair<CargoID, uint> *pair = capacities.Find(v->cargo_type);
02179 if (pair == capacities.End()) {
02180 pair = capacities.Append();
02181 pair->first = v->cargo_type;
02182 pair->second = v->cargo_cap - v->cargo.Count();
02183 } else {
02184 pair->second += v->cargo_cap - v->cargo.Count();
02185 }
02186 }
02187 }
02188
02189 uint Vehicle::GetConsistTotalCapacity() const
02190 {
02191 uint result = 0;
02192 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
02193 result += v->cargo_cap;
02194 }
02195 return result;
02196 }
02197
02204 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
02205 {
02206 CommandCost ret = CheckOwnership(this->owner);
02207 if (ret.Failed()) return ret;
02208
02209 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
02210 if (this->IsStoppedInDepot()) return CMD_ERROR;
02211
02212 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
02213 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
02214 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
02215
02216
02217
02218 if (flags & DC_EXEC) {
02219 this->current_order.SetDepotOrderType(ODTF_MANUAL);
02220 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
02221 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02222 }
02223 return CommandCost();
02224 }
02225
02226 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR;
02227 if (flags & DC_EXEC) {
02228
02229
02230 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
02231
02232 if (this->IsGroundVehicle()) {
02233 uint16 &gv_flags = this->GetGroundVehicleFlags();
02234 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02235 }
02236
02237 this->current_order.MakeDummy();
02238 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02239 }
02240 return CommandCost();
02241 }
02242
02243 TileIndex location;
02244 DestinationID destination;
02245 bool reverse;
02246 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};
02247 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
02248
02249 if (flags & DC_EXEC) {
02250 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
02251
02252 if (this->IsGroundVehicle()) {
02253 uint16 &gv_flags = this->GetGroundVehicleFlags();
02254 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02255 }
02256
02257 this->dest_tile = location;
02258 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
02259 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
02260 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02261
02262
02263 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
02264
02265 if (this->type == VEH_AIRCRAFT) {
02266 Aircraft *a = Aircraft::From(this);
02267 if (a->state == FLYING && a->targetairport != destination) {
02268
02269 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
02270 AircraftNextAirportPos_and_Order(a);
02271 }
02272 }
02273 }
02274
02275 return CommandCost();
02276
02277 }
02278
02283 void Vehicle::UpdateVisualEffect(bool allow_power_change)
02284 {
02285 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02286 const Engine *e = this->GetEngine();
02287
02288
02289 byte visual_effect;
02290 switch (e->type) {
02291 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
02292 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
02293 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
02294 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
02295 }
02296
02297
02298 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
02299 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
02300
02301 if (callback != CALLBACK_FAILED) {
02302 if (callback >= 0x100 && e->GetGRF()->grf_version >= 8) ErrorUnknownCallbackResult(e->GetGRFID(), CBID_VEHICLE_VISUAL_EFFECT, callback);
02303
02304 callback = GB(callback, 0, 8);
02305
02306
02307 if (callback == VE_DEFAULT) {
02308 assert(HasBit(callback, VE_DISABLE_EFFECT));
02309 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
02310 }
02311 visual_effect = callback;
02312 }
02313 }
02314
02315
02316 if (visual_effect == VE_DEFAULT ||
02317 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
02318
02319
02320 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
02321 if (visual_effect == VE_DEFAULT) {
02322 visual_effect = 1 << VE_DISABLE_EFFECT;
02323 } else {
02324 SetBit(visual_effect, VE_DISABLE_EFFECT);
02325 }
02326 } else {
02327 if (visual_effect == VE_DEFAULT) {
02328
02329 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
02330 }
02331 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
02332 }
02333 }
02334
02335 this->vcache.cached_vis_effect = visual_effect;
02336
02337 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
02338 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02339 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
02340 }
02341 }
02342
02343 static const int8 _vehicle_smoke_pos[8] = {
02344 1, 1, 1, 0, -1, -1, -1, 0
02345 };
02346
02351 void Vehicle::ShowVisualEffect() const
02352 {
02353 assert(this->IsPrimaryVehicle());
02354 bool sound = false;
02355
02356
02357
02358
02359
02360
02361 if (_settings_game.vehicle.smoke_amount == 0 ||
02362 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
02363 this->cur_speed < 2) {
02364 return;
02365 }
02366
02367 uint max_speed = this->vcache.cached_max_speed;
02368 if (this->type == VEH_TRAIN) {
02369 const Train *t = Train::From(this);
02370
02371
02372
02373
02374 if (HasBit(t->flags, VRF_REVERSING) ||
02375 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
02376 t->cur_speed >= t->Train::GetCurrentMaxSpeed())) {
02377 return;
02378 }
02379
02380 max_speed = min(max_speed, t->gcache.cached_max_track_speed);
02381 max_speed = min(max_speed, this->current_order.max_speed);
02382 }
02383 if (this->type == VEH_ROAD || this->type == VEH_SHIP) max_speed = min(max_speed, this->current_order.max_speed * 2);
02384
02385 const Vehicle *v = this;
02386
02387 do {
02388 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
02389 byte effect_type = GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
02390 bool disable_effect = HasBit(v->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
02391
02392
02393
02394
02395
02396
02397
02398
02399 if (disable_effect ||
02400 v->vehstatus & VS_HIDDEN ||
02401 (MayHaveBridgeAbove(v->tile) && IsBridgeAbove(v->tile)) ||
02402 IsDepotTile(v->tile) ||
02403 IsTunnelTile(v->tile) ||
02404 (v->type == VEH_TRAIN &&
02405 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
02406 continue;
02407 }
02408
02409
02410
02411
02412 if (v->type == VEH_TRAIN) effect_offset += (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
02413
02414 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
02415 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
02416
02417 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
02418 x = -x;
02419 y = -y;
02420 }
02421
02422 switch (effect_type) {
02423 case VE_TYPE_STEAM:
02424
02425
02426
02427
02428
02429 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / max_speed))) == 0) {
02430 CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
02431 sound = true;
02432 }
02433 break;
02434
02435 case VE_TYPE_DIESEL: {
02436
02437
02438
02439
02440
02441
02442
02443
02444
02445
02446
02447 int power_weight_effect = 0;
02448 if (v->type == VEH_TRAIN) {
02449 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
02450 }
02451 if (this->cur_speed < (max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
02452 Chance16((64 - ((this->cur_speed << 5) / max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
02453 CreateEffectVehicleRel(v, x, y, 10, EV_DIESEL_SMOKE);
02454 sound = true;
02455 }
02456 break;
02457 }
02458
02459 case VE_TYPE_ELECTRIC:
02460
02461
02462
02463
02464
02465
02466 if (GB(v->tick_counter, 0, 2) == 0 &&
02467 Chance16((6 - ((this->cur_speed << 2) / max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
02468 CreateEffectVehicleRel(v, x, y, 10, EV_ELECTRIC_SPARK);
02469 sound = true;
02470 }
02471 break;
02472
02473 default:
02474 break;
02475 }
02476 } while ((v = v->Next()) != NULL);
02477
02478 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
02479 }
02480
02485 void Vehicle::SetNext(Vehicle *next)
02486 {
02487 assert(this != next);
02488
02489 if (this->next != NULL) {
02490
02491 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02492 v->first = this->next;
02493 }
02494 this->next->previous = NULL;
02495 }
02496
02497 this->next = next;
02498
02499 if (this->next != NULL) {
02500
02501 if (this->next->previous != NULL) this->next->previous->next = NULL;
02502 this->next->previous = this;
02503 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02504 v->first = this->first;
02505 }
02506 }
02507 }
02508
02514 void Vehicle::AddToShared(Vehicle *shared_chain)
02515 {
02516 assert(this->previous_shared == NULL && this->next_shared == NULL);
02517
02518 if (shared_chain->orders.list == NULL) {
02519 assert(shared_chain->previous_shared == NULL);
02520 assert(shared_chain->next_shared == NULL);
02521 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
02522 }
02523
02524 this->next_shared = shared_chain->next_shared;
02525 this->previous_shared = shared_chain;
02526
02527 shared_chain->next_shared = this;
02528
02529 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
02530
02531 shared_chain->orders.list->AddVehicle(this);
02532 }
02533
02537 void Vehicle::RemoveFromShared()
02538 {
02539
02540
02541 bool were_first = (this->FirstShared() == this);
02542 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
02543
02544 this->orders.list->RemoveVehicle(this);
02545
02546 if (!were_first) {
02547
02548 this->previous_shared->next_shared = this->NextShared();
02549 }
02550
02551 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
02552
02553
02554 if (this->orders.list->GetNumVehicles() == 1) {
02555
02556 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
02557 InvalidateVehicleOrder(this->FirstShared(), 0);
02558 } else if (were_first) {
02559
02560
02561 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
02562 }
02563
02564 this->next_shared = NULL;
02565 this->previous_shared = NULL;
02566 }
02567
02568 void VehiclesYearlyLoop()
02569 {
02570 Vehicle *v;
02571 FOR_ALL_VEHICLES(v) {
02572 if (v->IsPrimaryVehicle()) {
02573
02574 Money profit = v->GetDisplayProfitThisYear();
02575 if (v->age >= 730 && profit < 0) {
02576 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
02577 SetDParam(0, v->index);
02578 SetDParam(1, profit);
02579 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_UNPROFITABLE, v->index);
02580 }
02581 AI::NewEvent(v->owner, new ScriptEventVehicleUnprofitable(v->index));
02582 }
02583
02584 v->profit_last_year = v->profit_this_year;
02585 v->profit_this_year = 0;
02586 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
02587 }
02588 }
02589 GroupStatistics::UpdateProfits();
02590 SetWindowClassesDirty(WC_TRAINS_LIST);
02591 SetWindowClassesDirty(WC_SHIPS_LIST);
02592 SetWindowClassesDirty(WC_ROADVEH_LIST);
02593 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
02594 }
02595
02596
02606 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
02607 {
02608 const Engine *e = Engine::GetIfValid(engine_type);
02609 assert(e != NULL);
02610
02611 switch (e->type) {
02612 case VEH_TRAIN:
02613 return (st->facilities & FACIL_TRAIN) != 0;
02614
02615 case VEH_ROAD:
02616
02617
02618
02619 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
02620
02621 case VEH_SHIP:
02622 return (st->facilities & FACIL_DOCK) != 0;
02623
02624 case VEH_AIRCRAFT:
02625 return (st->facilities & FACIL_AIRPORT) != 0 &&
02626 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
02627
02628 default:
02629 return false;
02630 }
02631 }
02632
02639 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
02640 {
02641 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
02642
02643 return CanVehicleUseStation(v->engine_type, st);
02644 }
02645
02651 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
02652 {
02653 assert(this->IsGroundVehicle());
02654 if (this->type == VEH_TRAIN) {
02655 return &Train::From(this)->gcache;
02656 } else {
02657 return &RoadVehicle::From(this)->gcache;
02658 }
02659 }
02660
02666 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
02667 {
02668 assert(this->IsGroundVehicle());
02669 if (this->type == VEH_TRAIN) {
02670 return &Train::From(this)->gcache;
02671 } else {
02672 return &RoadVehicle::From(this)->gcache;
02673 }
02674 }
02675
02681 uint16 &Vehicle::GetGroundVehicleFlags()
02682 {
02683 assert(this->IsGroundVehicle());
02684 if (this->type == VEH_TRAIN) {
02685 return Train::From(this)->gv_flags;
02686 } else {
02687 return RoadVehicle::From(this)->gv_flags;
02688 }
02689 }
02690
02696 const uint16 &Vehicle::GetGroundVehicleFlags() const
02697 {
02698 assert(this->IsGroundVehicle());
02699 if (this->type == VEH_TRAIN) {
02700 return Train::From(this)->gv_flags;
02701 } else {
02702 return RoadVehicle::From(this)->gv_flags;
02703 }
02704 }
02705
02714 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
02715 {
02716 if (v->type == VEH_TRAIN) {
02717 Train *u = Train::From(v);
02718
02719 u = u->GetFirstEnginePart();
02720
02721
02722 for (; u != NULL && num_vehicles > 0; num_vehicles--) {
02723 do {
02724
02725 set.Include(u->index);
02726
02727
02728 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
02729
02730 u = u->Next();
02731 } while (u != NULL && u->IsArticulatedPart());
02732 }
02733 }
02734 }