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
02014 this->RefreshNextHopsStats();
02015
02016
02017 this->last_loading_station = this->last_station_visited;
02018 } else {
02019
02020
02021
02022 this->last_loading_station = INVALID_STATION;
02023 }
02024 }
02025
02026 this->current_order.MakeLeaveStation();
02027 Station *st = Station::Get(this->last_station_visited);
02028 this->CancelReservation(INVALID_STATION, st);
02029 st->loading_vehicles.remove(this);
02030
02031 HideFillingPercent(&this->fill_percent_te_id);
02032
02033 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
02034
02035 if (IsTileType(this->tile, MP_STATION)) TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
02036
02037 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
02038 }
02039 }
02040
02047 void Vehicle::RefreshNextHopsStats()
02048 {
02049
02050 SmallMap<CargoID, uint, 1> capacities;
02051 for (Vehicle *v = this; v != NULL; v = v->Next()) {
02052 v->refit_cap = v->cargo_cap;
02053 if (v->refit_cap == 0) continue;
02054 SmallPair<CargoID, uint> *i = capacities.Find(v->cargo_type);
02055 if (i == capacities.End()) {
02056
02057 i = capacities.Append();
02058 i->first = v->cargo_type;
02059 i->second = v->cargo_cap;
02060 } else {
02061 i->second += v->cargo_cap;
02062 }
02063 }
02064
02065
02066 if (this->orders.list == NULL) return;
02067
02068 uint hops = 0;
02069 const Order *first = this->orders.list->GetNextStoppingOrder(this,
02070 this->GetOrder(this->cur_implicit_order_index), hops);
02071 const Order *cur = first;
02072 const Order *next = first;
02073 while (next != NULL && cur->CanLeaveWithCargo(true)) {
02074 next = this->orders.list->GetNextStoppingOrder(this,
02075 this->orders.list->GetNext(next), ++hops);
02076 if (next == NULL) break;
02077
02078 if (next->IsType(OT_GOTO_DEPOT)) {
02079
02080 CargoID new_cid = next->GetRefitCargo();
02081 byte new_subtype = next->GetRefitSubtype();
02082 for (Vehicle *v = this; v != NULL; v = v->Next()) {
02083 const Engine *e = Engine::Get(v->engine_type);
02084 if (!HasBit(e->info.refit_mask, new_cid)) continue;
02085
02086
02087 CargoID temp_cid = v->cargo_type;
02088 byte temp_subtype = v->cargo_subtype;
02089 v->cargo_type = new_cid;
02090 v->cargo_subtype = new_subtype;
02091
02092 uint16 mail_capacity = 0;
02093 uint amount = e->DetermineCapacity(v, &mail_capacity);
02094
02095
02096 v->cargo_type = temp_cid;
02097 v->cargo_subtype = temp_subtype;
02098
02099
02100 if (new_cid != v->cargo_type && v->refit_cap > 0) {
02101 capacities[v->cargo_type] -= v->refit_cap;
02102 v->refit_cap = 0;
02103 } else if (amount < v->refit_cap) {
02104 capacities[v->cargo_type] -= v->refit_cap - amount;
02105 v->refit_cap = amount;
02106 }
02107
02108
02109 if (v->type == VEH_AIRCRAFT) {
02110 Vehicle *u = v->Next();
02111 if (mail_capacity < u->refit_cap) {
02112 capacities[u->cargo_type] -= u->refit_cap - mail_capacity;
02113 u->refit_cap = mail_capacity;
02114 }
02115 break;
02116 }
02117 if (v->type == VEH_SHIP) break;
02118 }
02119 } else {
02120 StationID next_station = next->GetDestination();
02121 Station *st = Station::GetIfValid(cur->GetDestination());
02122 if (st != NULL && next_station != INVALID_STATION && next_station != st->index) {
02123 for (const SmallPair<CargoID, uint> *i = capacities.Begin(); i != capacities.End(); ++i) {
02124
02125 if (i->second > 0) IncreaseStats(st, i->first, next_station, i->second, UINT_MAX);
02126 }
02127 }
02128 cur = next;
02129 if (cur == first) break;
02130 }
02131 }
02132
02133 for (Vehicle *v = this; v != NULL; v = v->Next()) v->refit_cap = v->cargo_cap;
02134 }
02135
02141 void Vehicle::HandleLoading(bool mode)
02142 {
02143 switch (this->current_order.GetType()) {
02144 case OT_LOADING: {
02145 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
02146
02147
02148 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
02149
02150 this->PlayLeaveStationSound();
02151
02152 this->LeaveStation();
02153
02154
02155 const Order *order = this->GetOrder(this->cur_implicit_order_index);
02156 if (order == NULL ||
02157 (!order->IsType(OT_IMPLICIT) && !order->IsType(OT_GOTO_STATION)) ||
02158 order->GetDestination() != this->last_station_visited) {
02159 return;
02160 }
02161 break;
02162 }
02163
02164 case OT_DUMMY: break;
02165
02166 default: return;
02167 }
02168
02169 this->IncrementImplicitOrderIndex();
02170 }
02171
02176 void Vehicle::GetConsistFreeCapacities(SmallMap<CargoID, uint> &capacities) const
02177 {
02178 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
02179 if (v->cargo_cap == 0) continue;
02180 SmallPair<CargoID, uint> *pair = capacities.Find(v->cargo_type);
02181 if (pair == capacities.End()) {
02182 pair = capacities.Append();
02183 pair->first = v->cargo_type;
02184 pair->second = v->cargo_cap - v->cargo.Count();
02185 } else {
02186 pair->second += v->cargo_cap - v->cargo.Count();
02187 }
02188 }
02189 }
02190
02191 uint Vehicle::GetConsistTotalCapacity() const
02192 {
02193 uint result = 0;
02194 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
02195 result += v->cargo_cap;
02196 }
02197 return result;
02198 }
02199
02206 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
02207 {
02208 CommandCost ret = CheckOwnership(this->owner);
02209 if (ret.Failed()) return ret;
02210
02211 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
02212 if (this->IsStoppedInDepot()) return CMD_ERROR;
02213
02214 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
02215 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
02216 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
02217
02218
02219
02220 if (flags & DC_EXEC) {
02221 this->current_order.SetDepotOrderType(ODTF_MANUAL);
02222 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
02223 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02224 }
02225 return CommandCost();
02226 }
02227
02228 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR;
02229 if (flags & DC_EXEC) {
02230
02231
02232 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
02233
02234 if (this->IsGroundVehicle()) {
02235 uint16 &gv_flags = this->GetGroundVehicleFlags();
02236 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02237 }
02238
02239 this->current_order.MakeDummy();
02240 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02241 }
02242 return CommandCost();
02243 }
02244
02245 TileIndex location;
02246 DestinationID destination;
02247 bool reverse;
02248 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};
02249 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
02250
02251 if (flags & DC_EXEC) {
02252 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
02253
02254 if (this->IsGroundVehicle()) {
02255 uint16 &gv_flags = this->GetGroundVehicleFlags();
02256 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02257 }
02258
02259 this->dest_tile = location;
02260 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
02261 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
02262 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02263
02264
02265 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
02266
02267 if (this->type == VEH_AIRCRAFT) {
02268 Aircraft *a = Aircraft::From(this);
02269 if (a->state == FLYING && a->targetairport != destination) {
02270
02271 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
02272 AircraftNextAirportPos_and_Order(a);
02273 }
02274 }
02275 }
02276
02277 return CommandCost();
02278
02279 }
02280
02285 void Vehicle::UpdateVisualEffect(bool allow_power_change)
02286 {
02287 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02288 const Engine *e = this->GetEngine();
02289
02290
02291 byte visual_effect;
02292 switch (e->type) {
02293 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
02294 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
02295 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
02296 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
02297 }
02298
02299
02300 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
02301 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
02302
02303 if (callback != CALLBACK_FAILED) {
02304 if (callback >= 0x100 && e->GetGRF()->grf_version >= 8) ErrorUnknownCallbackResult(e->GetGRFID(), CBID_VEHICLE_VISUAL_EFFECT, callback);
02305
02306 callback = GB(callback, 0, 8);
02307
02308
02309 if (callback == VE_DEFAULT) {
02310 assert(HasBit(callback, VE_DISABLE_EFFECT));
02311 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
02312 }
02313 visual_effect = callback;
02314 }
02315 }
02316
02317
02318 if (visual_effect == VE_DEFAULT ||
02319 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
02320
02321
02322 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
02323 if (visual_effect == VE_DEFAULT) {
02324 visual_effect = 1 << VE_DISABLE_EFFECT;
02325 } else {
02326 SetBit(visual_effect, VE_DISABLE_EFFECT);
02327 }
02328 } else {
02329 if (visual_effect == VE_DEFAULT) {
02330
02331 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
02332 }
02333 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
02334 }
02335 }
02336
02337 this->vcache.cached_vis_effect = visual_effect;
02338
02339 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
02340 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02341 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
02342 }
02343 }
02344
02345 static const int8 _vehicle_smoke_pos[8] = {
02346 1, 1, 1, 0, -1, -1, -1, 0
02347 };
02348
02353 void Vehicle::ShowVisualEffect() const
02354 {
02355 assert(this->IsPrimaryVehicle());
02356 bool sound = false;
02357
02358
02359
02360
02361
02362
02363 if (_settings_game.vehicle.smoke_amount == 0 ||
02364 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
02365 this->cur_speed < 2) {
02366 return;
02367 }
02368
02369 uint max_speed = this->vcache.cached_max_speed;
02370 if (this->type == VEH_TRAIN) {
02371 const Train *t = Train::From(this);
02372
02373
02374
02375
02376 if (HasBit(t->flags, VRF_REVERSING) ||
02377 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
02378 t->cur_speed >= t->Train::GetCurrentMaxSpeed())) {
02379 return;
02380 }
02381
02382 max_speed = min(max_speed, t->gcache.cached_max_track_speed);
02383 max_speed = min(max_speed, this->current_order.max_speed);
02384 }
02385 if (this->type == VEH_ROAD || this->type == VEH_SHIP) max_speed = min(max_speed, this->current_order.max_speed * 2);
02386
02387 const Vehicle *v = this;
02388
02389 do {
02390 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
02391 byte effect_type = GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
02392 bool disable_effect = HasBit(v->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
02393
02394
02395
02396
02397
02398
02399
02400
02401 if (disable_effect ||
02402 v->vehstatus & VS_HIDDEN ||
02403 (MayHaveBridgeAbove(v->tile) && IsBridgeAbove(v->tile)) ||
02404 IsDepotTile(v->tile) ||
02405 IsTunnelTile(v->tile) ||
02406 (v->type == VEH_TRAIN &&
02407 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
02408 continue;
02409 }
02410
02411
02412
02413
02414 if (v->type == VEH_TRAIN) effect_offset += (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
02415
02416 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
02417 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
02418
02419 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
02420 x = -x;
02421 y = -y;
02422 }
02423
02424 switch (effect_type) {
02425 case VE_TYPE_STEAM:
02426
02427
02428
02429
02430
02431 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / max_speed))) == 0) {
02432 CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
02433 sound = true;
02434 }
02435 break;
02436
02437 case VE_TYPE_DIESEL: {
02438
02439
02440
02441
02442
02443
02444
02445
02446
02447
02448
02449 int power_weight_effect = 0;
02450 if (v->type == VEH_TRAIN) {
02451 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
02452 }
02453 if (this->cur_speed < (max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
02454 Chance16((64 - ((this->cur_speed << 5) / max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
02455 CreateEffectVehicleRel(v, x, y, 10, EV_DIESEL_SMOKE);
02456 sound = true;
02457 }
02458 break;
02459 }
02460
02461 case VE_TYPE_ELECTRIC:
02462
02463
02464
02465
02466
02467
02468 if (GB(v->tick_counter, 0, 2) == 0 &&
02469 Chance16((6 - ((this->cur_speed << 2) / max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
02470 CreateEffectVehicleRel(v, x, y, 10, EV_ELECTRIC_SPARK);
02471 sound = true;
02472 }
02473 break;
02474
02475 default:
02476 break;
02477 }
02478 } while ((v = v->Next()) != NULL);
02479
02480 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
02481 }
02482
02487 void Vehicle::SetNext(Vehicle *next)
02488 {
02489 assert(this != next);
02490
02491 if (this->next != NULL) {
02492
02493 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02494 v->first = this->next;
02495 }
02496 this->next->previous = NULL;
02497 }
02498
02499 this->next = next;
02500
02501 if (this->next != NULL) {
02502
02503 if (this->next->previous != NULL) this->next->previous->next = NULL;
02504 this->next->previous = this;
02505 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02506 v->first = this->first;
02507 }
02508 }
02509 }
02510
02516 void Vehicle::AddToShared(Vehicle *shared_chain)
02517 {
02518 assert(this->previous_shared == NULL && this->next_shared == NULL);
02519
02520 if (shared_chain->orders.list == NULL) {
02521 assert(shared_chain->previous_shared == NULL);
02522 assert(shared_chain->next_shared == NULL);
02523 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
02524 }
02525
02526 this->next_shared = shared_chain->next_shared;
02527 this->previous_shared = shared_chain;
02528
02529 shared_chain->next_shared = this;
02530
02531 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
02532
02533 shared_chain->orders.list->AddVehicle(this);
02534 }
02535
02539 void Vehicle::RemoveFromShared()
02540 {
02541
02542
02543 bool were_first = (this->FirstShared() == this);
02544 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
02545
02546 this->orders.list->RemoveVehicle(this);
02547
02548 if (!were_first) {
02549
02550 this->previous_shared->next_shared = this->NextShared();
02551 }
02552
02553 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
02554
02555
02556 if (this->orders.list->GetNumVehicles() == 1) {
02557
02558 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
02559 InvalidateVehicleOrder(this->FirstShared(), 0);
02560 } else if (were_first) {
02561
02562
02563 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
02564 }
02565
02566 this->next_shared = NULL;
02567 this->previous_shared = NULL;
02568 }
02569
02570 void VehiclesYearlyLoop()
02571 {
02572 Vehicle *v;
02573 FOR_ALL_VEHICLES(v) {
02574 if (v->IsPrimaryVehicle()) {
02575
02576 Money profit = v->GetDisplayProfitThisYear();
02577 if (v->age >= 730 && profit < 0) {
02578 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
02579 SetDParam(0, v->index);
02580 SetDParam(1, profit);
02581 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_UNPROFITABLE, v->index);
02582 }
02583 AI::NewEvent(v->owner, new ScriptEventVehicleUnprofitable(v->index));
02584 }
02585
02586 v->profit_last_year = v->profit_this_year;
02587 v->profit_this_year = 0;
02588 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
02589 }
02590 }
02591 GroupStatistics::UpdateProfits();
02592 SetWindowClassesDirty(WC_TRAINS_LIST);
02593 SetWindowClassesDirty(WC_SHIPS_LIST);
02594 SetWindowClassesDirty(WC_ROADVEH_LIST);
02595 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
02596 }
02597
02598
02608 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
02609 {
02610 const Engine *e = Engine::GetIfValid(engine_type);
02611 assert(e != NULL);
02612
02613 switch (e->type) {
02614 case VEH_TRAIN:
02615 return (st->facilities & FACIL_TRAIN) != 0;
02616
02617 case VEH_ROAD:
02618
02619
02620
02621 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
02622
02623 case VEH_SHIP:
02624 return (st->facilities & FACIL_DOCK) != 0;
02625
02626 case VEH_AIRCRAFT:
02627 return (st->facilities & FACIL_AIRPORT) != 0 &&
02628 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
02629
02630 default:
02631 return false;
02632 }
02633 }
02634
02641 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
02642 {
02643 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
02644
02645 return CanVehicleUseStation(v->engine_type, st);
02646 }
02647
02653 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
02654 {
02655 assert(this->IsGroundVehicle());
02656 if (this->type == VEH_TRAIN) {
02657 return &Train::From(this)->gcache;
02658 } else {
02659 return &RoadVehicle::From(this)->gcache;
02660 }
02661 }
02662
02668 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
02669 {
02670 assert(this->IsGroundVehicle());
02671 if (this->type == VEH_TRAIN) {
02672 return &Train::From(this)->gcache;
02673 } else {
02674 return &RoadVehicle::From(this)->gcache;
02675 }
02676 }
02677
02683 uint16 &Vehicle::GetGroundVehicleFlags()
02684 {
02685 assert(this->IsGroundVehicle());
02686 if (this->type == VEH_TRAIN) {
02687 return Train::From(this)->gv_flags;
02688 } else {
02689 return RoadVehicle::From(this)->gv_flags;
02690 }
02691 }
02692
02698 const uint16 &Vehicle::GetGroundVehicleFlags() const
02699 {
02700 assert(this->IsGroundVehicle());
02701 if (this->type == VEH_TRAIN) {
02702 return Train::From(this)->gv_flags;
02703 } else {
02704 return RoadVehicle::From(this)->gv_flags;
02705 }
02706 }
02707
02716 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
02717 {
02718 if (v->type == VEH_TRAIN) {
02719 Train *u = Train::From(v);
02720
02721 u = u->GetFirstEnginePart();
02722
02723
02724 for (; u != NULL && num_vehicles > 0; num_vehicles--) {
02725 do {
02726
02727 set.Include(u->index);
02728
02729
02730 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
02731
02732 u = u->Next();
02733 } while (u != NULL && u->IsArticulatedPart());
02734 }
02735 }
02736 }