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 *front, 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 bool is_loading = front->current_order.IsType(OT_LOADING);
01245
01246
01247 const Station *st = Station::GetIfValid(front->last_station_visited);
01248 assert(colour == NULL || (st != NULL && is_loading));
01249
01250 bool order_no_load = is_loading && (front->current_order.GetLoadType() & OLFB_NO_LOAD);
01251 bool order_full_load = is_loading && (front->current_order.GetLoadType() & OLFB_FULL_LOAD);
01252
01253
01254 for (const Vehicle *v = front; v != NULL; v = v->Next()) {
01255 count += v->cargo.OnboardCount();
01256 max += v->cargo_cap;
01257 if (v->cargo_cap != 0 && colour != NULL) {
01258 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
01259 loading |= !order_no_load &&
01260 (order_full_load || HasBit(st->goods[v->cargo_type].acceptance_pickup, GoodsEntry::GES_PICKUP)) &&
01261 !HasBit(v->vehicle_flags, VF_LOADING_FINISHED) && !HasBit(v->vehicle_flags, VF_STOP_LOADING);
01262 cars++;
01263 }
01264 }
01265
01266 if (colour != NULL) {
01267 if (unloading == 0 && loading) {
01268 *colour = STR_PERCENT_UP;
01269 } else if (unloading == 0 && !loading) {
01270 *colour = STR_PERCENT_NONE;
01271 } else if (cars == unloading || !loading) {
01272 *colour = STR_PERCENT_DOWN;
01273 } else {
01274 *colour = STR_PERCENT_UP_DOWN;
01275 }
01276 }
01277
01278
01279 if (max == 0) return 100;
01280
01281
01282 return (count * 100) / max;
01283 }
01284
01289 void VehicleEnterDepot(Vehicle *v)
01290 {
01291
01292 assert(v == v->First());
01293
01294 switch (v->type) {
01295 case VEH_TRAIN: {
01296 Train *t = Train::From(v);
01297 SetWindowClassesDirty(WC_TRAINS_LIST);
01298
01299 SetDepotReservation(t->tile, false);
01300 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
01301
01302 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
01303 t->wait_counter = 0;
01304 t->force_proceed = TFP_NONE;
01305 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
01306 t->ConsistChanged(true);
01307 break;
01308 }
01309
01310 case VEH_ROAD:
01311 SetWindowClassesDirty(WC_ROADVEH_LIST);
01312 break;
01313
01314 case VEH_SHIP: {
01315 SetWindowClassesDirty(WC_SHIPS_LIST);
01316 Ship *ship = Ship::From(v);
01317 ship->state = TRACK_BIT_DEPOT;
01318 ship->UpdateCache();
01319 ship->UpdateViewport(true, true);
01320 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01321 break;
01322 }
01323
01324 case VEH_AIRCRAFT:
01325 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01326 HandleAircraftEnterHangar(Aircraft::From(v));
01327 break;
01328 default: NOT_REACHED();
01329 }
01330 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01331
01332 if (v->type != VEH_TRAIN) {
01333
01334
01335 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01336 }
01337 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01338
01339 v->vehstatus |= VS_HIDDEN;
01340 v->cur_speed = 0;
01341
01342 VehicleServiceInDepot(v);
01343
01344 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01345
01346 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01347 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01348
01349 const Order *real_order = v->GetOrder(v->cur_real_order_index);
01350 Order t = v->current_order;
01351 v->current_order.MakeDummy();
01352
01353
01354
01355 if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
01356 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
01357 (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
01358
01359 return;
01360 }
01361
01362 if (t.IsRefit()) {
01363 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01364 CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
01365 cur_company.Restore();
01366
01367 if (cost.Failed()) {
01368 _vehicles_to_autoreplace[v] = false;
01369 if (v->owner == _local_company) {
01370
01371 SetDParam(0, v->index);
01372 AddVehicleAdviceNewsItem(STR_NEWS_ORDER_REFIT_FAILED, v->index);
01373 }
01374 } else if (cost.GetCost() != 0) {
01375 v->profit_this_year -= cost.GetCost() << 8;
01376 if (v->owner == _local_company) {
01377 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01378 }
01379 }
01380 }
01381
01382 if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01383
01384 v->DeleteUnreachedImplicitOrders();
01385 UpdateVehicleTimetable(v, true);
01386 v->IncrementImplicitOrderIndex();
01387 }
01388 if (t.GetDepotActionType() & ODATFB_HALT) {
01389
01390 _vehicles_to_autoreplace[v] = false;
01391 if (v->owner == _local_company) {
01392 SetDParam(0, v->index);
01393 AddVehicleAdviceNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, v->index);
01394 }
01395 AI::NewEvent(v->owner, new ScriptEventVehicleWaitingInDepot(v->index));
01396 }
01397 }
01398 }
01399
01400
01406 void VehicleUpdatePosition(Vehicle *v)
01407 {
01408 UpdateVehicleTileHash(v, false);
01409 }
01410
01417 void VehicleUpdateViewport(Vehicle *v, bool dirty)
01418 {
01419 int img = v->cur_image;
01420 Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
01421 const Sprite *spr = GetSprite(img, ST_NORMAL);
01422
01423 pt.x += spr->x_offs;
01424 pt.y += spr->y_offs;
01425
01426 UpdateVehicleViewportHash(v, pt.x, pt.y);
01427
01428 Rect old_coord = v->coord;
01429 v->coord.left = pt.x;
01430 v->coord.top = pt.y;
01431 v->coord.right = pt.x + spr->width + 2 * ZOOM_LVL_BASE;
01432 v->coord.bottom = pt.y + spr->height + 2 * ZOOM_LVL_BASE;
01433
01434 if (dirty) {
01435 if (old_coord.left == INVALID_COORD) {
01436 MarkSingleVehicleDirty(v);
01437 } else {
01438 MarkAllViewportsDirty(
01439 min(old_coord.left, v->coord.left),
01440 min(old_coord.top, v->coord.top),
01441 max(old_coord.right, v->coord.right) + 1 * ZOOM_LVL_BASE,
01442 max(old_coord.bottom, v->coord.bottom) + 1 * ZOOM_LVL_BASE
01443 );
01444 }
01445 }
01446 }
01447
01452 void VehicleUpdatePositionAndViewport(Vehicle *v)
01453 {
01454 VehicleUpdatePosition(v);
01455 VehicleUpdateViewport(v, true);
01456 }
01457
01462 void MarkSingleVehicleDirty(const Vehicle *v)
01463 {
01464 MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1 * ZOOM_LVL_BASE, v->coord.bottom + 1 * ZOOM_LVL_BASE);
01465 }
01466
01472 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01473 {
01474 static const int8 _delta_coord[16] = {
01475 -1,-1,-1, 0, 1, 1, 1, 0,
01476 -1, 0, 1, 1, 1, 0,-1,-1,
01477 };
01478
01479 int x = v->x_pos + _delta_coord[v->direction];
01480 int y = v->y_pos + _delta_coord[v->direction + 8];
01481
01482 GetNewVehiclePosResult gp;
01483 gp.x = x;
01484 gp.y = y;
01485 gp.old_tile = v->tile;
01486 gp.new_tile = TileVirtXY(x, y);
01487 return gp;
01488 }
01489
01490 static const Direction _new_direction_table[] = {
01491 DIR_N, DIR_NW, DIR_W,
01492 DIR_NE, DIR_SE, DIR_SW,
01493 DIR_E, DIR_SE, DIR_S
01494 };
01495
01496 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01497 {
01498 int i = 0;
01499
01500 if (y >= v->y_pos) {
01501 if (y != v->y_pos) i += 3;
01502 i += 3;
01503 }
01504
01505 if (x >= v->x_pos) {
01506 if (x != v->x_pos) i++;
01507 i++;
01508 }
01509
01510 Direction dir = v->direction;
01511
01512 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01513 if (dirdiff == DIRDIFF_SAME) return dir;
01514 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01515 }
01516
01526 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01527 {
01528 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01529 }
01530
01538 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01539 {
01540
01541 const Vehicle *v;
01542 FOR_ALL_VEHICLES(v) {
01543 if (v->type == type && v->owner == owner) {
01544 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01545 }
01546 }
01547
01548 if (this->maxid == 0) return;
01549
01550
01551
01552
01553 this->cache = CallocT<bool>(this->maxid + 2);
01554
01555
01556 FOR_ALL_VEHICLES(v) {
01557 if (v->type == type && v->owner == owner) {
01558 this->cache[v->unitnumber] = true;
01559 }
01560 }
01561 }
01562
01564 UnitID FreeUnitIDGenerator::NextID()
01565 {
01566 if (this->maxid <= this->curid) return ++this->curid;
01567
01568 while (this->cache[++this->curid]) { }
01569
01570 return this->curid;
01571 }
01572
01578 UnitID GetFreeUnitNumber(VehicleType type)
01579 {
01580
01581 uint max_veh;
01582 switch (type) {
01583 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
01584 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
01585 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
01586 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
01587 default: NOT_REACHED();
01588 }
01589
01590 const Company *c = Company::Get(_current_company);
01591 if (c->group_all[type].num_vehicle >= max_veh) return UINT16_MAX;
01592
01593 FreeUnitIDGenerator gen(type, _current_company);
01594
01595 return gen.NextID();
01596 }
01597
01598
01607 bool CanBuildVehicleInfrastructure(VehicleType type)
01608 {
01609 assert(IsCompanyBuildableVehicleType(type));
01610
01611 if (!Company::IsValidID(_local_company)) return false;
01612 if (!_settings_client.gui.disable_unsuitable_building) return true;
01613
01614 UnitID max;
01615 switch (type) {
01616 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
01617 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
01618 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
01619 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01620 default: NOT_REACHED();
01621 }
01622
01623
01624 if (max > 0) {
01625
01626 const Engine *e;
01627 FOR_ALL_ENGINES_OF_TYPE(e, type) {
01628 if (HasBit(e->company_avail, _local_company)) return true;
01629 }
01630 return false;
01631 }
01632
01633
01634 const Vehicle *v;
01635 FOR_ALL_VEHICLES(v) {
01636 if (v->owner == _local_company && v->type == type) return true;
01637 }
01638
01639 return false;
01640 }
01641
01642
01650 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
01651 {
01652 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01653 const Engine *e = Engine::Get(engine_type);
01654 switch (e->type) {
01655 default: NOT_REACHED();
01656 case VEH_TRAIN:
01657 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
01658
01659
01660 engine_type = parent_engine_type;
01661 e = Engine::Get(engine_type);
01662
01663 }
01664
01665 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01666 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01667 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
01668 if (!CargoSpec::Get(cargo_type)->is_freight) {
01669 if (parent_engine_type == INVALID_ENGINE) {
01670 return LS_PASSENGER_WAGON_STEAM;
01671 } else {
01672 switch (RailVehInfo(parent_engine_type)->engclass) {
01673 default: NOT_REACHED();
01674 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
01675 case EC_DIESEL: return LS_PASSENGER_WAGON_DIESEL;
01676 case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
01677 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
01678 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
01679 }
01680 }
01681 } else {
01682 return LS_FREIGHT_WAGON;
01683 }
01684 } else {
01685 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
01686
01687 switch (e->u.rail.engclass) {
01688 default: NOT_REACHED();
01689 case EC_STEAM: return LS_STEAM;
01690 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
01691 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
01692 case EC_MONORAIL: return LS_MONORAIL;
01693 case EC_MAGLEV: return LS_MAGLEV;
01694 }
01695 }
01696
01697 case VEH_ROAD:
01698
01699 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01700 engine_type = parent_engine_type;
01701 e = Engine::Get(engine_type);
01702 cargo_type = v->First()->cargo_type;
01703 }
01704 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01705 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01706
01707
01708 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
01709
01710 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01711 } else {
01712
01713 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01714 }
01715
01716 case VEH_SHIP:
01717 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01718 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01719 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01720
01721 case VEH_AIRCRAFT:
01722 switch (e->u.air.subtype) {
01723 case AIR_HELI: return LS_HELICOPTER;
01724 case AIR_CTOL: return LS_SMALL_PLANE;
01725 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
01726 default: NOT_REACHED();
01727 }
01728 }
01729 }
01730
01740 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
01741 {
01742 const Company *c = Company::Get(company);
01743 LiveryScheme scheme = LS_DEFAULT;
01744
01745
01746
01747 if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
01748
01749 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
01750
01751
01752 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01753 }
01754
01755 return &c->livery[scheme];
01756 }
01757
01758
01759 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01760 {
01761 PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01762
01763
01764 if (map != PAL_NONE) return map;
01765
01766 const Engine *e = Engine::Get(engine_type);
01767
01768
01769 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
01770 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01771
01772 if (callback != CALLBACK_FAILED) {
01773 assert_compile(PAL_NONE == 0);
01774 map = GB(callback, 0, 14);
01775
01776
01777 if (!HasBit(callback, 14)) {
01778
01779 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01780 return map;
01781 }
01782 }
01783 }
01784
01785 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
01786
01787 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
01788
01789
01790 if (!Company::IsValidID(company)) return map;
01791
01792 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
01793
01794 map += livery->colour1;
01795 if (twocc) map += livery->colour2 * 16;
01796
01797
01798 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01799 return map;
01800 }
01801
01808 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
01809 {
01810 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01811 }
01812
01818 PaletteID GetVehiclePalette(const Vehicle *v)
01819 {
01820 if (v->IsGroundVehicle()) {
01821 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
01822 }
01823
01824 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01825 }
01826
01830 void Vehicle::DeleteUnreachedImplicitOrders()
01831 {
01832 if (this->IsGroundVehicle()) {
01833 uint16 &gv_flags = this->GetGroundVehicleFlags();
01834 if (HasBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)) {
01835
01836 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
01837 this->cur_implicit_order_index = this->cur_real_order_index;
01838 InvalidateVehicleOrder(this, 0);
01839 return;
01840 }
01841 }
01842
01843 const Order *order = this->GetOrder(this->cur_implicit_order_index);
01844 while (order != NULL) {
01845 if (this->cur_implicit_order_index == this->cur_real_order_index) break;
01846
01847 if (order->IsType(OT_IMPLICIT)) {
01848
01849 order = order->next;
01850 DeleteOrder(this, this->cur_implicit_order_index);
01851 } else {
01852
01853 order = order->next;
01854 this->cur_implicit_order_index++;
01855 }
01856
01857
01858 if (order == NULL) {
01859 order = this->GetOrder(0);
01860 this->cur_implicit_order_index = 0;
01861 }
01862 }
01863 }
01864
01869 void Vehicle::BeginLoading()
01870 {
01871 assert(IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP);
01872
01873 if (this->current_order.IsType(OT_GOTO_STATION) &&
01874 this->current_order.GetDestination() == this->last_station_visited) {
01875 this->DeleteUnreachedImplicitOrders();
01876
01877
01878 this->current_order.MakeLoading(true);
01879 UpdateVehicleTimetable(this, true);
01880
01881
01882
01883
01884
01885
01886 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
01887
01888 } else {
01889
01890
01891
01892
01893
01894 Order *in_list = this->GetOrder(this->cur_implicit_order_index);
01895 if (this->IsGroundVehicle() && in_list != NULL &&
01896 (!in_list->IsType(OT_IMPLICIT) ||
01897 in_list->GetDestination() != this->last_station_visited)) {
01898 bool suppress_implicit_orders = HasBit(this->GetGroundVehicleFlags(), GVF_SUPPRESS_IMPLICIT_ORDERS);
01899
01900 Order *prev_order = this->cur_implicit_order_index > 0 ? this->GetOrder(this->cur_implicit_order_index - 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : NULL);
01901 if (prev_order == NULL ||
01902 (!prev_order->IsType(OT_IMPLICIT) && !prev_order->IsType(OT_GOTO_STATION)) ||
01903 prev_order->GetDestination() != this->last_station_visited) {
01904
01905
01906
01907 int target_index = this->cur_implicit_order_index;
01908 bool found = false;
01909 while (target_index != this->cur_real_order_index) {
01910 const Order *order = this->GetOrder(target_index);
01911 if (order->IsType(OT_IMPLICIT) && order->GetDestination() == this->last_station_visited) {
01912 found = true;
01913 break;
01914 }
01915 target_index++;
01916 if (target_index >= this->orders.list->GetNumOrders()) target_index = 0;
01917 assert(target_index != this->cur_implicit_order_index);
01918 }
01919
01920 if (found) {
01921 if (suppress_implicit_orders) {
01922
01923 this->cur_implicit_order_index = target_index;
01924 InvalidateVehicleOrder(this, 0);
01925 } else {
01926
01927 const Order *order = this->GetOrder(this->cur_implicit_order_index);
01928 while (!order->IsType(OT_IMPLICIT) || order->GetDestination() != this->last_station_visited) {
01929 if (order->IsType(OT_IMPLICIT)) {
01930
01931 order = order->next;
01932 DeleteOrder(this, this->cur_implicit_order_index);
01933 } else {
01934
01935 order = order->next;
01936 this->cur_implicit_order_index++;
01937 }
01938
01939
01940 if (order == NULL) {
01941 order = this->GetOrder(0);
01942 this->cur_implicit_order_index = 0;
01943 }
01944 assert(order != NULL);
01945 }
01946 }
01947 } else if (!suppress_implicit_orders && this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID && Order::CanAllocateItem()) {
01948
01949 Order *implicit_order = new Order();
01950 implicit_order->MakeImplicit(this->last_station_visited);
01951 InsertOrder(this, implicit_order, this->cur_implicit_order_index);
01952 if (this->cur_implicit_order_index > 0) --this->cur_implicit_order_index;
01953
01954
01955
01956 uint16 &gv_flags = this->GetGroundVehicleFlags();
01957 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
01958 }
01959 }
01960 }
01961 this->current_order.MakeLoading(false);
01962 }
01963
01964 if (this->last_loading_station != INVALID_STATION &&
01965 this->last_loading_station != this->last_station_visited &&
01966 ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
01967 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0)) {
01968 IncreaseStats(Station::Get(this->last_loading_station), this, this->last_station_visited);
01969 }
01970
01971 PrepareUnload(this);
01972
01973 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
01974 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
01975 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01976 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
01977
01978 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
01979 this->cur_speed = 0;
01980 this->MarkDirty();
01981 }
01982
01987 void Vehicle::CancelReservation(StationID next, Station *st)
01988 {
01989 for (Vehicle *v = this; v != NULL; v = v->next) {
01990 VehicleCargoList &cargo = v->cargo;
01991 if (cargo.ReservedCount() > 0) {
01992 DEBUG(misc, 1, "cancelling cargo reservation");
01993 GoodsEntry &ge = st->goods[v->cargo_type];
01994 cargo.Unreserve(next, &ge.cargo);
01995 SetBit(ge.acceptance_pickup, GoodsEntry::GES_PICKUP);
01996 }
01997 }
01998 }
01999
02004 void Vehicle::LeaveStation()
02005 {
02006 assert(this->current_order.IsType(OT_LOADING));
02007
02008 delete this->cargo_payment;
02009
02010
02011 if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
02012
02013 if ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
02014 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
02015 if (this->current_order.CanLeaveWithCargo(this->last_loading_station != INVALID_STATION)) {
02016
02017
02018
02019 this->RefreshNextHopsStats();
02020
02021
02022 this->last_loading_station = this->last_station_visited;
02023 } else {
02024
02025
02026 this->last_loading_station = INVALID_STATION;
02027 }
02028 }
02029
02030 this->current_order.MakeLeaveStation();
02031 Station *st = Station::Get(this->last_station_visited);
02032 this->CancelReservation(INVALID_STATION, st);
02033 st->loading_vehicles.remove(this);
02034
02035 HideFillingPercent(&this->fill_percent_te_id);
02036
02037 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
02038
02039 if (IsTileType(this->tile, MP_STATION)) TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
02040
02041 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
02042 }
02043 }
02044
02051 void Vehicle::RefreshNextHopsStats()
02052 {
02053
02054 SmallMap<CargoID, uint, 1> capacities;
02055 for (Vehicle *v = this; v != NULL; v = v->Next()) {
02056 v->refit_cap = v->cargo_cap;
02057 if (v->refit_cap == 0) continue;
02058 capacities[v->cargo_type] += v->cargo_cap;
02059 }
02060
02061
02062 if (this->orders.list == NULL) return;
02063
02064 uint hops = 0;
02065 const Order *first = this->orders.list->GetNextStoppingOrder(this,
02066 this->GetOrder(this->cur_implicit_order_index), hops);
02067 const Order *cur = first;
02068 const Order *next = first;
02069 while (next != NULL && cur->CanLeaveWithCargo(true)) {
02070 next = this->orders.list->GetNextStoppingOrder(this,
02071 this->orders.list->GetNext(next), ++hops);
02072 if (next == NULL) break;
02073
02074 if (next->IsType(OT_GOTO_DEPOT)) {
02075
02076 CargoID new_cid = next->GetRefitCargo();
02077 byte new_subtype = next->GetRefitSubtype();
02078 for (Vehicle *v = this; v != NULL; v = v->Next()) {
02079 const Engine *e = Engine::Get(v->engine_type);
02080 if (!HasBit(e->info.refit_mask, new_cid)) continue;
02081
02082
02083 CargoID temp_cid = v->cargo_type;
02084 byte temp_subtype = v->cargo_subtype;
02085 v->cargo_type = new_cid;
02086 v->cargo_subtype = new_subtype;
02087
02088 uint16 mail_capacity = 0;
02089 uint amount = e->DetermineCapacity(v, &mail_capacity);
02090
02091
02092 v->cargo_type = temp_cid;
02093 v->cargo_subtype = temp_subtype;
02094
02095
02096 if (new_cid != v->cargo_type && v->refit_cap > 0) {
02097 capacities[v->cargo_type] -= v->refit_cap;
02098 v->refit_cap = 0;
02099 } else if (amount < v->refit_cap) {
02100 capacities[v->cargo_type] -= v->refit_cap - amount;
02101 v->refit_cap = amount;
02102 }
02103
02104
02105 if (v->type == VEH_AIRCRAFT) {
02106 Vehicle *u = v->Next();
02107 if (mail_capacity < u->refit_cap) {
02108 capacities[u->cargo_type] -= u->refit_cap - mail_capacity;
02109 u->refit_cap = mail_capacity;
02110 }
02111 break;
02112 }
02113 }
02114 } else {
02115 StationID next_station = next->GetDestination();
02116 Station *st = Station::GetIfValid(cur->GetDestination());
02117 if (st != NULL && next_station != INVALID_STATION && next_station != st->index) {
02118 for (const SmallPair<CargoID, uint> *i = capacities.Begin(); i != capacities.End(); ++i) {
02119
02120 if (i->second > 0) IncreaseStats(st, i->first, next_station, i->second, UINT_MAX);
02121 }
02122 }
02123 cur = next;
02124 if (cur == first) break;
02125 }
02126 }
02127
02128 for (Vehicle *v = this; v != NULL; v = v->Next()) v->refit_cap = v->cargo_cap;
02129 }
02130
02136 void Vehicle::HandleLoading(bool mode)
02137 {
02138 switch (this->current_order.GetType()) {
02139 case OT_LOADING: {
02140 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
02141
02142
02143 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
02144
02145 this->PlayLeaveStationSound();
02146
02147 this->LeaveStation();
02148
02149
02150 const Order *order = this->GetOrder(this->cur_implicit_order_index);
02151 if (order == NULL ||
02152 (!order->IsType(OT_IMPLICIT) && !order->IsType(OT_GOTO_STATION)) ||
02153 order->GetDestination() != this->last_station_visited) {
02154 return;
02155 }
02156 break;
02157 }
02158
02159 case OT_DUMMY: break;
02160
02161 default: return;
02162 }
02163
02164 this->IncrementImplicitOrderIndex();
02165 }
02166
02171 void Vehicle::GetConsistFreeCapacities(SmallMap<CargoID, uint> &capacities) const
02172 {
02173 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
02174 if (v->cargo_cap == 0) continue;
02175 SmallPair<CargoID, uint> *pair = capacities.Find(v->cargo_type);
02176 if (pair == capacities.End()) {
02177 pair = capacities.Append();
02178 pair->first = v->cargo_type;
02179 pair->second = v->cargo_cap - v->cargo.Count();
02180 } else {
02181 pair->second += v->cargo_cap - v->cargo.Count();
02182 }
02183 }
02184 }
02185
02186 uint Vehicle::GetConsistTotalCapacity() const
02187 {
02188 uint result = 0;
02189 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
02190 result += v->cargo_cap;
02191 }
02192 return result;
02193 }
02194
02201 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
02202 {
02203 CommandCost ret = CheckOwnership(this->owner);
02204 if (ret.Failed()) return ret;
02205
02206 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
02207 if (this->IsStoppedInDepot()) return CMD_ERROR;
02208
02209 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
02210 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
02211 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
02212
02213
02214
02215 if (flags & DC_EXEC) {
02216 this->current_order.SetDepotOrderType(ODTF_MANUAL);
02217 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
02218 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02219 }
02220 return CommandCost();
02221 }
02222
02223 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR;
02224 if (flags & DC_EXEC) {
02225
02226
02227 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
02228
02229 if (this->IsGroundVehicle()) {
02230 uint16 &gv_flags = this->GetGroundVehicleFlags();
02231 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02232 }
02233
02234 this->current_order.MakeDummy();
02235 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02236 }
02237 return CommandCost();
02238 }
02239
02240 TileIndex location;
02241 DestinationID destination;
02242 bool reverse;
02243 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};
02244 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
02245
02246 if (flags & DC_EXEC) {
02247 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
02248
02249 if (this->IsGroundVehicle()) {
02250 uint16 &gv_flags = this->GetGroundVehicleFlags();
02251 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02252 }
02253
02254 this->dest_tile = location;
02255 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
02256 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
02257 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02258
02259
02260 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
02261
02262 if (this->type == VEH_AIRCRAFT) {
02263 Aircraft *a = Aircraft::From(this);
02264 if (a->state == FLYING && a->targetairport != destination) {
02265
02266 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
02267 AircraftNextAirportPos_and_Order(a);
02268 }
02269 }
02270 }
02271
02272 return CommandCost();
02273
02274 }
02275
02280 void Vehicle::UpdateVisualEffect(bool allow_power_change)
02281 {
02282 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02283 const Engine *e = this->GetEngine();
02284
02285
02286 byte visual_effect;
02287 switch (e->type) {
02288 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
02289 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
02290 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
02291 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
02292 }
02293
02294
02295 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
02296 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
02297
02298 if (callback != CALLBACK_FAILED) {
02299 if (callback >= 0x100 && e->GetGRF()->grf_version >= 8) ErrorUnknownCallbackResult(e->GetGRFID(), CBID_VEHICLE_VISUAL_EFFECT, callback);
02300
02301 callback = GB(callback, 0, 8);
02302
02303
02304 if (callback == VE_DEFAULT) {
02305 assert(HasBit(callback, VE_DISABLE_EFFECT));
02306 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
02307 }
02308 visual_effect = callback;
02309 }
02310 }
02311
02312
02313 if (visual_effect == VE_DEFAULT ||
02314 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
02315
02316
02317 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
02318 if (visual_effect == VE_DEFAULT) {
02319 visual_effect = 1 << VE_DISABLE_EFFECT;
02320 } else {
02321 SetBit(visual_effect, VE_DISABLE_EFFECT);
02322 }
02323 } else {
02324 if (visual_effect == VE_DEFAULT) {
02325
02326 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
02327 }
02328 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
02329 }
02330 }
02331
02332 this->vcache.cached_vis_effect = visual_effect;
02333
02334 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
02335 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02336 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
02337 }
02338 }
02339
02340 static const int8 _vehicle_smoke_pos[8] = {
02341 1, 1, 1, 0, -1, -1, -1, 0
02342 };
02343
02348 void Vehicle::ShowVisualEffect() const
02349 {
02350 assert(this->IsPrimaryVehicle());
02351 bool sound = false;
02352
02353
02354
02355
02356
02357
02358 if (_settings_game.vehicle.smoke_amount == 0 ||
02359 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
02360 this->cur_speed < 2) {
02361 return;
02362 }
02363
02364 uint max_speed = this->vcache.cached_max_speed;
02365 if (this->type == VEH_TRAIN) {
02366 const Train *t = Train::From(this);
02367
02368
02369
02370
02371 if (HasBit(t->flags, VRF_REVERSING) ||
02372 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
02373 t->cur_speed >= t->Train::GetCurrentMaxSpeed())) {
02374 return;
02375 }
02376
02377 max_speed = min(max_speed, t->gcache.cached_max_track_speed);
02378 max_speed = min(max_speed, this->current_order.max_speed);
02379 }
02380 if (this->type == VEH_ROAD || this->type == VEH_SHIP) max_speed = min(max_speed, this->current_order.max_speed * 2);
02381
02382 const Vehicle *v = this;
02383
02384 do {
02385 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
02386 byte effect_type = GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
02387 bool disable_effect = HasBit(v->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
02388
02389
02390
02391
02392
02393
02394
02395
02396 if (disable_effect ||
02397 v->vehstatus & VS_HIDDEN ||
02398 (MayHaveBridgeAbove(v->tile) && IsBridgeAbove(v->tile)) ||
02399 IsDepotTile(v->tile) ||
02400 IsTunnelTile(v->tile) ||
02401 (v->type == VEH_TRAIN &&
02402 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
02403 continue;
02404 }
02405
02406
02407
02408
02409 if (v->type == VEH_TRAIN) effect_offset += (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
02410
02411 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
02412 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
02413
02414 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
02415 x = -x;
02416 y = -y;
02417 }
02418
02419 switch (effect_type) {
02420 case VE_TYPE_STEAM:
02421
02422
02423
02424
02425
02426 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / max_speed))) == 0) {
02427 CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
02428 sound = true;
02429 }
02430 break;
02431
02432 case VE_TYPE_DIESEL: {
02433
02434
02435
02436
02437
02438
02439
02440
02441
02442
02443
02444 int power_weight_effect = 0;
02445 if (v->type == VEH_TRAIN) {
02446 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
02447 }
02448 if (this->cur_speed < (max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
02449 Chance16((64 - ((this->cur_speed << 5) / max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
02450 CreateEffectVehicleRel(v, x, y, 10, EV_DIESEL_SMOKE);
02451 sound = true;
02452 }
02453 break;
02454 }
02455
02456 case VE_TYPE_ELECTRIC:
02457
02458
02459
02460
02461
02462
02463 if (GB(v->tick_counter, 0, 2) == 0 &&
02464 Chance16((6 - ((this->cur_speed << 2) / max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
02465 CreateEffectVehicleRel(v, x, y, 10, EV_ELECTRIC_SPARK);
02466 sound = true;
02467 }
02468 break;
02469
02470 default:
02471 break;
02472 }
02473 } while ((v = v->Next()) != NULL);
02474
02475 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
02476 }
02477
02482 void Vehicle::SetNext(Vehicle *next)
02483 {
02484 assert(this != next);
02485
02486 if (this->next != NULL) {
02487
02488 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02489 v->first = this->next;
02490 }
02491 this->next->previous = NULL;
02492 }
02493
02494 this->next = next;
02495
02496 if (this->next != NULL) {
02497
02498 if (this->next->previous != NULL) this->next->previous->next = NULL;
02499 this->next->previous = this;
02500 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02501 v->first = this->first;
02502 }
02503 }
02504 }
02505
02511 void Vehicle::AddToShared(Vehicle *shared_chain)
02512 {
02513 assert(this->previous_shared == NULL && this->next_shared == NULL);
02514
02515 if (shared_chain->orders.list == NULL) {
02516 assert(shared_chain->previous_shared == NULL);
02517 assert(shared_chain->next_shared == NULL);
02518 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
02519 }
02520
02521 this->next_shared = shared_chain->next_shared;
02522 this->previous_shared = shared_chain;
02523
02524 shared_chain->next_shared = this;
02525
02526 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
02527
02528 shared_chain->orders.list->AddVehicle(this);
02529 }
02530
02534 void Vehicle::RemoveFromShared()
02535 {
02536
02537
02538 bool were_first = (this->FirstShared() == this);
02539 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
02540
02541 this->orders.list->RemoveVehicle(this);
02542
02543 if (!were_first) {
02544
02545 this->previous_shared->next_shared = this->NextShared();
02546 }
02547
02548 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
02549
02550
02551 if (this->orders.list->GetNumVehicles() == 1) {
02552
02553 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
02554 InvalidateVehicleOrder(this->FirstShared(), 0);
02555 } else if (were_first) {
02556
02557
02558 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
02559 }
02560
02561 this->next_shared = NULL;
02562 this->previous_shared = NULL;
02563 }
02564
02565 void VehiclesYearlyLoop()
02566 {
02567 Vehicle *v;
02568 FOR_ALL_VEHICLES(v) {
02569 if (v->IsPrimaryVehicle()) {
02570
02571 Money profit = v->GetDisplayProfitThisYear();
02572 if (v->age >= 730 && profit < 0) {
02573 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
02574 SetDParam(0, v->index);
02575 SetDParam(1, profit);
02576 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_UNPROFITABLE, v->index);
02577 }
02578 AI::NewEvent(v->owner, new ScriptEventVehicleUnprofitable(v->index));
02579 }
02580
02581 v->profit_last_year = v->profit_this_year;
02582 v->profit_this_year = 0;
02583 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
02584 }
02585 }
02586 GroupStatistics::UpdateProfits();
02587 SetWindowClassesDirty(WC_TRAINS_LIST);
02588 SetWindowClassesDirty(WC_SHIPS_LIST);
02589 SetWindowClassesDirty(WC_ROADVEH_LIST);
02590 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
02591 }
02592
02593
02603 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
02604 {
02605 const Engine *e = Engine::GetIfValid(engine_type);
02606 assert(e != NULL);
02607
02608 switch (e->type) {
02609 case VEH_TRAIN:
02610 return (st->facilities & FACIL_TRAIN) != 0;
02611
02612 case VEH_ROAD:
02613
02614
02615
02616 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
02617
02618 case VEH_SHIP:
02619 return (st->facilities & FACIL_DOCK) != 0;
02620
02621 case VEH_AIRCRAFT:
02622 return (st->facilities & FACIL_AIRPORT) != 0 &&
02623 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
02624
02625 default:
02626 return false;
02627 }
02628 }
02629
02636 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
02637 {
02638 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
02639
02640 return CanVehicleUseStation(v->engine_type, st);
02641 }
02642
02648 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
02649 {
02650 assert(this->IsGroundVehicle());
02651 if (this->type == VEH_TRAIN) {
02652 return &Train::From(this)->gcache;
02653 } else {
02654 return &RoadVehicle::From(this)->gcache;
02655 }
02656 }
02657
02663 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
02664 {
02665 assert(this->IsGroundVehicle());
02666 if (this->type == VEH_TRAIN) {
02667 return &Train::From(this)->gcache;
02668 } else {
02669 return &RoadVehicle::From(this)->gcache;
02670 }
02671 }
02672
02678 uint16 &Vehicle::GetGroundVehicleFlags()
02679 {
02680 assert(this->IsGroundVehicle());
02681 if (this->type == VEH_TRAIN) {
02682 return Train::From(this)->gv_flags;
02683 } else {
02684 return RoadVehicle::From(this)->gv_flags;
02685 }
02686 }
02687
02693 const uint16 &Vehicle::GetGroundVehicleFlags() const
02694 {
02695 assert(this->IsGroundVehicle());
02696 if (this->type == VEH_TRAIN) {
02697 return Train::From(this)->gv_flags;
02698 } else {
02699 return RoadVehicle::From(this)->gv_flags;
02700 }
02701 }
02702
02711 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
02712 {
02713 if (v->type == VEH_TRAIN) {
02714 Train *u = Train::From(v);
02715
02716 u = u->GetFirstEnginePart();
02717
02718
02719 for (; u != NULL && num_vehicles > 0; num_vehicles--) {
02720 do {
02721
02722 set.Include(u->index);
02723
02724
02725 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
02726
02727 u = u->Next();
02728 } while (u != NULL && u->IsArticulatedPart());
02729 }
02730 }
02731 }