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)) {
00841
00842 TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32);
00843 v->MarkDirty();
00844 }
00845 if (HasBit(callback, 1)) v->colourmap = PAL_NONE;
00846
00847 if (callback & ~3) ErrorUnknownCallbackResult(v->GetGRFID(), CBID_VEHICLE_32DAY_CALLBACK, callback);
00848 }
00849 }
00850
00851
00852 v->OnNewDay();
00853 }
00854 }
00855
00856 void CallVehicleTicks()
00857 {
00858 _vehicles_to_autoreplace.Clear();
00859
00860 RunVehicleDayProc();
00861
00862 Station *st;
00863 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
00864
00865 Vehicle *v;
00866 FOR_ALL_VEHICLES(v) {
00867
00868 if (!v->Tick()) {
00869 assert(Vehicle::Get(vehicle_index) == NULL);
00870 continue;
00871 }
00872
00873 assert(Vehicle::Get(vehicle_index) == v);
00874
00875 switch (v->type) {
00876 default: break;
00877
00878 case VEH_TRAIN:
00879 case VEH_ROAD:
00880 case VEH_AIRCRAFT:
00881 case VEH_SHIP:
00882 if (v->vcache.cached_cargo_age_period != 0) {
00883 v->cargo_age_counter = min(v->cargo_age_counter, v->vcache.cached_cargo_age_period);
00884 if (--v->cargo_age_counter == 0) {
00885 v->cargo.AgeCargo();
00886 v->cargo_age_counter = v->vcache.cached_cargo_age_period;
00887 }
00888 }
00889
00890 if (v->type == VEH_TRAIN && Train::From(v)->IsWagon()) continue;
00891 if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
00892 if (v->type == VEH_ROAD && !RoadVehicle::From(v)->IsFrontEngine()) continue;
00893
00894 v->motion_counter += v->cur_speed;
00895
00896 if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
00897
00898
00899 if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
00900 }
00901 }
00902
00903 Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
00904 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
00905 v = it->first;
00906
00907 cur_company.Change(v->owner);
00908
00909
00910
00911
00912 if (it->second) v->vehstatus &= ~VS_STOPPED;
00913
00914
00915 int x = v->x_pos;
00916 int y = v->y_pos;
00917 int z = v->z_pos;
00918
00919 const Company *c = Company::Get(_current_company);
00920 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
00921 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
00922 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
00923
00924 if (!IsLocalCompany()) continue;
00925
00926 if (res.Succeeded()) {
00927 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
00928 continue;
00929 }
00930
00931 StringID error_message = res.GetErrorMessage();
00932 if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
00933
00934 if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
00935
00936 StringID message;
00937 if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
00938 message = error_message;
00939 } else {
00940 message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
00941 }
00942
00943 SetDParam(0, v->index);
00944 SetDParam(1, error_message);
00945 AddVehicleAdviceNewsItem(message, v->index);
00946 }
00947
00948 cur_company.Restore();
00949 }
00950
00955 static void DoDrawVehicle(const Vehicle *v)
00956 {
00957 SpriteID image = v->cur_image;
00958 PaletteID pal = PAL_NONE;
00959
00960 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
00961
00962
00963 bool shadowed = (v->vehstatus & VS_SHADOW) != 0;
00964
00965 if (v->type == VEH_EFFECT) {
00966
00967
00968 TransparencyOption to = EffectVehicle::From(v)->GetTransparencyOption();
00969 if (to != TO_INVALID && (IsTransparencySet(to) || IsInvisibilitySet(to))) return;
00970 }
00971
00972 AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
00973 v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs);
00974 }
00975
00980 void ViewportAddVehicles(DrawPixelInfo *dpi)
00981 {
00982
00983 const int l = dpi->left;
00984 const int r = dpi->left + dpi->width;
00985 const int t = dpi->top;
00986 const int b = dpi->top + dpi->height;
00987
00988
00989 int xl, xu, yl, yu;
00990
00991 if (dpi->width + (70 * ZOOM_LVL_BASE) < (1 << (7 + 6 + ZOOM_LVL_SHIFT))) {
00992 xl = GB(l - (70 * ZOOM_LVL_BASE), 7 + ZOOM_LVL_SHIFT, 6);
00993 xu = GB(r, 7 + ZOOM_LVL_SHIFT, 6);
00994 } else {
00995
00996 xl = 0;
00997 xu = 0x3F;
00998 }
00999
01000 if (dpi->height + (70 * ZOOM_LVL_BASE) < (1 << (6 + 6 + ZOOM_LVL_SHIFT))) {
01001 yl = GB(t - (70 * ZOOM_LVL_BASE), 6 + ZOOM_LVL_SHIFT, 6) << 6;
01002 yu = GB(b, 6 + ZOOM_LVL_SHIFT, 6) << 6;
01003 } else {
01004
01005 yl = 0;
01006 yu = 0x3F << 6;
01007 }
01008
01009 for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
01010 for (int x = xl;; x = (x + 1) & 0x3F) {
01011 const Vehicle *v = _vehicle_viewport_hash[x + y];
01012
01013 while (v != NULL) {
01014 if (!(v->vehstatus & VS_HIDDEN) &&
01015 l <= v->coord.right &&
01016 t <= v->coord.bottom &&
01017 r >= v->coord.left &&
01018 b >= v->coord.top) {
01019 DoDrawVehicle(v);
01020 }
01021 v = v->hash_viewport_next;
01022 }
01023
01024 if (x == xu) break;
01025 }
01026
01027 if (y == yu) break;
01028 }
01029 }
01030
01038 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
01039 {
01040 Vehicle *found = NULL, *v;
01041 uint dist, best_dist = UINT_MAX;
01042
01043 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
01044
01045 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
01046 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
01047
01048 FOR_ALL_VEHICLES(v) {
01049 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
01050 x >= v->coord.left && x <= v->coord.right &&
01051 y >= v->coord.top && y <= v->coord.bottom) {
01052
01053 dist = max(
01054 abs(((v->coord.left + v->coord.right) >> 1) - x),
01055 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
01056 );
01057
01058 if (dist < best_dist) {
01059 found = v;
01060 best_dist = dist;
01061 }
01062 }
01063 }
01064
01065 return found;
01066 }
01067
01072 void DecreaseVehicleValue(Vehicle *v)
01073 {
01074 v->value -= v->value >> 8;
01075 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01076 }
01077
01078 static const byte _breakdown_chance[64] = {
01079 3, 3, 3, 3, 3, 3, 3, 3,
01080 4, 4, 5, 5, 6, 6, 7, 7,
01081 8, 8, 9, 9, 10, 10, 11, 11,
01082 12, 13, 13, 13, 13, 14, 15, 16,
01083 17, 19, 21, 25, 28, 31, 34, 37,
01084 40, 44, 48, 52, 56, 60, 64, 68,
01085 72, 80, 90, 100, 110, 120, 130, 140,
01086 150, 170, 190, 210, 230, 250, 250, 250,
01087 };
01088
01089 void CheckVehicleBreakdown(Vehicle *v)
01090 {
01091 int rel, rel_old;
01092
01093
01094 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
01095 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01096
01097 if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
01098 _settings_game.difficulty.vehicle_breakdowns < 1 ||
01099 v->cur_speed < 5 || _game_mode == GM_MENU) {
01100 return;
01101 }
01102
01103 uint32 r = Random();
01104
01105
01106 int chance = v->breakdown_chance + 1;
01107 if (Chance16I(1, 25, r)) chance += 25;
01108 v->breakdown_chance = min(255, chance);
01109
01110
01111 rel = v->reliability;
01112 if (v->type == VEH_SHIP) rel += 0x6666;
01113
01114
01115 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
01116
01117
01118 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
01119 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
01120 v->breakdown_delay = GB(r, 24, 7) + 0x80;
01121 v->breakdown_chance = 0;
01122 }
01123 }
01124
01131 bool Vehicle::HandleBreakdown()
01132 {
01133
01134
01135
01136
01137
01138 switch (this->breakdown_ctr) {
01139 case 0:
01140 return false;
01141
01142 case 2:
01143 this->breakdown_ctr = 1;
01144
01145 if (this->breakdowns_since_last_service != 255) {
01146 this->breakdowns_since_last_service++;
01147 }
01148
01149 if (this->type == VEH_AIRCRAFT) {
01150
01151 this->vehstatus |= VS_AIRCRAFT_BROKEN;
01152 } else {
01153 this->cur_speed = 0;
01154
01155 if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
01156 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
01157 (this->type == VEH_TRAIN ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
01158 (this->type == VEH_TRAIN ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
01159 }
01160
01161 if (!(this->vehstatus & VS_HIDDEN) && !HasBit(EngInfo(this->engine_type)->misc_flags, EF_NO_BREAKDOWN_SMOKE)) {
01162 EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
01163 if (u != NULL) u->animation_state = this->breakdown_delay * 2;
01164 }
01165 }
01166
01167 this->MarkDirty();
01168 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01169 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01170
01171
01172 case 1:
01173
01174 if (this->type == VEH_AIRCRAFT) return false;
01175
01176
01177 if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
01178 if (--this->breakdown_delay == 0) {
01179 this->breakdown_ctr = 0;
01180 this->MarkDirty();
01181 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01182 }
01183 }
01184 return true;
01185
01186 default:
01187 if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
01188 return false;
01189 }
01190 }
01191
01196 void AgeVehicle(Vehicle *v)
01197 {
01198 if (v->age < MAX_DAY) {
01199 v->age++;
01200 if (v->IsPrimaryVehicle() && v->age == VEHICLE_PROFIT_MIN_AGE + 1) GroupStatistics::VehicleReachedProfitAge(v);
01201 }
01202
01203 if (!v->IsPrimaryVehicle() && (v->type != VEH_TRAIN || !Train::From(v)->IsEngine())) return;
01204
01205 int age = v->age - v->max_age;
01206 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
01207 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
01208 v->reliability_spd_dec <<= 1;
01209 }
01210
01211 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01212
01213
01214 if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
01215
01216
01217 if (Company::Get(v->owner)->settings.engine_renew && v->GetEngine()->company_avail != 0) return;
01218
01219 StringID str;
01220 if (age == -DAYS_IN_LEAP_YEAR) {
01221 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
01222 } else if (age == 0) {
01223 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
01224 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
01225 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
01226 } else {
01227 return;
01228 }
01229
01230 SetDParam(0, v->index);
01231 AddVehicleAdviceNewsItem(str, v->index);
01232 }
01233
01240 uint8 CalcPercentVehicleFilled(const Vehicle *front, StringID *colour)
01241 {
01242 int count = 0;
01243 int max = 0;
01244 int cars = 0;
01245 int unloading = 0;
01246 bool loading = false;
01247
01248 bool is_loading = front->current_order.IsType(OT_LOADING);
01249
01250
01251 const Station *st = Station::GetIfValid(front->last_station_visited);
01252 assert(colour == NULL || (st != NULL && is_loading));
01253
01254 bool order_no_load = is_loading && (front->current_order.GetLoadType() & OLFB_NO_LOAD);
01255 bool order_full_load = is_loading && (front->current_order.GetLoadType() & OLFB_FULL_LOAD);
01256
01257
01258 for (const Vehicle *v = front; v != NULL; v = v->Next()) {
01259 count += v->cargo.OnboardCount();
01260 max += v->cargo_cap;
01261 if (v->cargo_cap != 0 && colour != NULL) {
01262 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
01263 loading |= !order_no_load &&
01264 (order_full_load || HasBit(st->goods[v->cargo_type].acceptance_pickup, GoodsEntry::GES_PICKUP)) &&
01265 !HasBit(v->vehicle_flags, VF_LOADING_FINISHED) && !HasBit(v->vehicle_flags, VF_STOP_LOADING);
01266 cars++;
01267 }
01268 }
01269
01270 if (colour != NULL) {
01271 if (unloading == 0 && loading) {
01272 *colour = STR_PERCENT_UP;
01273 } else if (unloading == 0 && !loading) {
01274 *colour = STR_PERCENT_NONE;
01275 } else if (cars == unloading || !loading) {
01276 *colour = STR_PERCENT_DOWN;
01277 } else {
01278 *colour = STR_PERCENT_UP_DOWN;
01279 }
01280 }
01281
01282
01283 if (max == 0) return 100;
01284
01285
01286 return (count * 100) / max;
01287 }
01288
01293 void VehicleEnterDepot(Vehicle *v)
01294 {
01295
01296 assert(v == v->First());
01297
01298 switch (v->type) {
01299 case VEH_TRAIN: {
01300 Train *t = Train::From(v);
01301 SetWindowClassesDirty(WC_TRAINS_LIST);
01302
01303 SetDepotReservation(t->tile, false);
01304 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
01305
01306 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
01307 t->wait_counter = 0;
01308 t->force_proceed = TFP_NONE;
01309 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
01310 t->ConsistChanged(true);
01311 break;
01312 }
01313
01314 case VEH_ROAD:
01315 SetWindowClassesDirty(WC_ROADVEH_LIST);
01316 break;
01317
01318 case VEH_SHIP: {
01319 SetWindowClassesDirty(WC_SHIPS_LIST);
01320 Ship *ship = Ship::From(v);
01321 ship->state = TRACK_BIT_DEPOT;
01322 ship->UpdateCache();
01323 ship->UpdateViewport(true, true);
01324 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01325 break;
01326 }
01327
01328 case VEH_AIRCRAFT:
01329 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01330 HandleAircraftEnterHangar(Aircraft::From(v));
01331 break;
01332 default: NOT_REACHED();
01333 }
01334 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01335
01336 if (v->type != VEH_TRAIN) {
01337
01338
01339 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01340 }
01341 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01342
01343 v->vehstatus |= VS_HIDDEN;
01344 v->cur_speed = 0;
01345
01346 VehicleServiceInDepot(v);
01347
01348
01349 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01350 v->MarkDirty();
01351
01352 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01353 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01354
01355 const Order *real_order = v->GetOrder(v->cur_real_order_index);
01356 Order t = v->current_order;
01357 v->current_order.MakeDummy();
01358
01359
01360
01361 if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
01362 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
01363 (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
01364
01365 return;
01366 }
01367
01368 if (t.IsRefit()) {
01369 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01370 CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
01371 cur_company.Restore();
01372
01373 if (cost.Failed()) {
01374 _vehicles_to_autoreplace[v] = false;
01375 if (v->owner == _local_company) {
01376
01377 SetDParam(0, v->index);
01378 AddVehicleAdviceNewsItem(STR_NEWS_ORDER_REFIT_FAILED, v->index);
01379 }
01380 } else if (cost.GetCost() != 0) {
01381 v->profit_this_year -= cost.GetCost() << 8;
01382 if (v->owner == _local_company) {
01383 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01384 }
01385 }
01386 }
01387
01388 if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01389
01390 v->DeleteUnreachedImplicitOrders();
01391 UpdateVehicleTimetable(v, true);
01392 v->IncrementImplicitOrderIndex();
01393 }
01394 if (t.GetDepotActionType() & ODATFB_HALT) {
01395
01396 _vehicles_to_autoreplace[v] = false;
01397 if (v->owner == _local_company) {
01398 SetDParam(0, v->index);
01399 AddVehicleAdviceNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, v->index);
01400 }
01401 AI::NewEvent(v->owner, new ScriptEventVehicleWaitingInDepot(v->index));
01402 }
01403 }
01404 }
01405
01406
01412 void VehicleUpdatePosition(Vehicle *v)
01413 {
01414 UpdateVehicleTileHash(v, false);
01415 }
01416
01423 void VehicleUpdateViewport(Vehicle *v, bool dirty)
01424 {
01425 int img = v->cur_image;
01426 Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
01427 const Sprite *spr = GetSprite(img, ST_NORMAL);
01428
01429 pt.x += spr->x_offs;
01430 pt.y += spr->y_offs;
01431
01432 UpdateVehicleViewportHash(v, pt.x, pt.y);
01433
01434 Rect old_coord = v->coord;
01435 v->coord.left = pt.x;
01436 v->coord.top = pt.y;
01437 v->coord.right = pt.x + spr->width + 2 * ZOOM_LVL_BASE;
01438 v->coord.bottom = pt.y + spr->height + 2 * ZOOM_LVL_BASE;
01439
01440 if (dirty) {
01441 if (old_coord.left == INVALID_COORD) {
01442 MarkSingleVehicleDirty(v);
01443 } else {
01444 MarkAllViewportsDirty(
01445 min(old_coord.left, v->coord.left),
01446 min(old_coord.top, v->coord.top),
01447 max(old_coord.right, v->coord.right) + 1 * ZOOM_LVL_BASE,
01448 max(old_coord.bottom, v->coord.bottom) + 1 * ZOOM_LVL_BASE
01449 );
01450 }
01451 }
01452 }
01453
01458 void VehicleUpdatePositionAndViewport(Vehicle *v)
01459 {
01460 VehicleUpdatePosition(v);
01461 VehicleUpdateViewport(v, true);
01462 }
01463
01468 void MarkSingleVehicleDirty(const Vehicle *v)
01469 {
01470 MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1 * ZOOM_LVL_BASE, v->coord.bottom + 1 * ZOOM_LVL_BASE);
01471 }
01472
01478 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01479 {
01480 static const int8 _delta_coord[16] = {
01481 -1,-1,-1, 0, 1, 1, 1, 0,
01482 -1, 0, 1, 1, 1, 0,-1,-1,
01483 };
01484
01485 int x = v->x_pos + _delta_coord[v->direction];
01486 int y = v->y_pos + _delta_coord[v->direction + 8];
01487
01488 GetNewVehiclePosResult gp;
01489 gp.x = x;
01490 gp.y = y;
01491 gp.old_tile = v->tile;
01492 gp.new_tile = TileVirtXY(x, y);
01493 return gp;
01494 }
01495
01496 static const Direction _new_direction_table[] = {
01497 DIR_N, DIR_NW, DIR_W,
01498 DIR_NE, DIR_SE, DIR_SW,
01499 DIR_E, DIR_SE, DIR_S
01500 };
01501
01502 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01503 {
01504 int i = 0;
01505
01506 if (y >= v->y_pos) {
01507 if (y != v->y_pos) i += 3;
01508 i += 3;
01509 }
01510
01511 if (x >= v->x_pos) {
01512 if (x != v->x_pos) i++;
01513 i++;
01514 }
01515
01516 Direction dir = v->direction;
01517
01518 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01519 if (dirdiff == DIRDIFF_SAME) return dir;
01520 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01521 }
01522
01532 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01533 {
01534 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01535 }
01536
01544 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01545 {
01546
01547 const Vehicle *v;
01548 FOR_ALL_VEHICLES(v) {
01549 if (v->type == type && v->owner == owner) {
01550 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01551 }
01552 }
01553
01554 if (this->maxid == 0) return;
01555
01556
01557
01558
01559 this->cache = CallocT<bool>(this->maxid + 2);
01560
01561
01562 FOR_ALL_VEHICLES(v) {
01563 if (v->type == type && v->owner == owner) {
01564 this->cache[v->unitnumber] = true;
01565 }
01566 }
01567 }
01568
01570 UnitID FreeUnitIDGenerator::NextID()
01571 {
01572 if (this->maxid <= this->curid) return ++this->curid;
01573
01574 while (this->cache[++this->curid]) { }
01575
01576 return this->curid;
01577 }
01578
01584 UnitID GetFreeUnitNumber(VehicleType type)
01585 {
01586
01587 uint max_veh;
01588 switch (type) {
01589 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
01590 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
01591 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
01592 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
01593 default: NOT_REACHED();
01594 }
01595
01596 const Company *c = Company::Get(_current_company);
01597 if (c->group_all[type].num_vehicle >= max_veh) return UINT16_MAX;
01598
01599 FreeUnitIDGenerator gen(type, _current_company);
01600
01601 return gen.NextID();
01602 }
01603
01604
01613 bool CanBuildVehicleInfrastructure(VehicleType type)
01614 {
01615 assert(IsCompanyBuildableVehicleType(type));
01616
01617 if (!Company::IsValidID(_local_company)) return false;
01618 if (!_settings_client.gui.disable_unsuitable_building) return true;
01619
01620 UnitID max;
01621 switch (type) {
01622 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
01623 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
01624 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
01625 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01626 default: NOT_REACHED();
01627 }
01628
01629
01630 if (max > 0) {
01631
01632 const Engine *e;
01633 FOR_ALL_ENGINES_OF_TYPE(e, type) {
01634 if (HasBit(e->company_avail, _local_company)) return true;
01635 }
01636 return false;
01637 }
01638
01639
01640 const Vehicle *v;
01641 FOR_ALL_VEHICLES(v) {
01642 if (v->owner == _local_company && v->type == type) return true;
01643 }
01644
01645 return false;
01646 }
01647
01648
01656 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
01657 {
01658 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01659 const Engine *e = Engine::Get(engine_type);
01660 switch (e->type) {
01661 default: NOT_REACHED();
01662 case VEH_TRAIN:
01663 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
01664
01665
01666 engine_type = parent_engine_type;
01667 e = Engine::Get(engine_type);
01668
01669 }
01670
01671 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01672 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01673 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
01674 if (!CargoSpec::Get(cargo_type)->is_freight) {
01675 if (parent_engine_type == INVALID_ENGINE) {
01676 return LS_PASSENGER_WAGON_STEAM;
01677 } else {
01678 switch (RailVehInfo(parent_engine_type)->engclass) {
01679 default: NOT_REACHED();
01680 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
01681 case EC_DIESEL: return LS_PASSENGER_WAGON_DIESEL;
01682 case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
01683 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
01684 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
01685 }
01686 }
01687 } else {
01688 return LS_FREIGHT_WAGON;
01689 }
01690 } else {
01691 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
01692
01693 switch (e->u.rail.engclass) {
01694 default: NOT_REACHED();
01695 case EC_STEAM: return LS_STEAM;
01696 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
01697 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
01698 case EC_MONORAIL: return LS_MONORAIL;
01699 case EC_MAGLEV: return LS_MAGLEV;
01700 }
01701 }
01702
01703 case VEH_ROAD:
01704
01705 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01706 engine_type = parent_engine_type;
01707 e = Engine::Get(engine_type);
01708 cargo_type = v->First()->cargo_type;
01709 }
01710 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01711 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01712
01713
01714 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
01715
01716 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01717 } else {
01718
01719 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01720 }
01721
01722 case VEH_SHIP:
01723 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01724 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01725 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01726
01727 case VEH_AIRCRAFT:
01728 switch (e->u.air.subtype) {
01729 case AIR_HELI: return LS_HELICOPTER;
01730 case AIR_CTOL: return LS_SMALL_PLANE;
01731 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
01732 default: NOT_REACHED();
01733 }
01734 }
01735 }
01736
01746 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
01747 {
01748 const Company *c = Company::Get(company);
01749 LiveryScheme scheme = LS_DEFAULT;
01750
01751
01752
01753 if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
01754
01755 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
01756
01757
01758 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01759 }
01760
01761 return &c->livery[scheme];
01762 }
01763
01764
01765 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01766 {
01767 PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01768
01769
01770 if (map != PAL_NONE) return map;
01771
01772 const Engine *e = Engine::Get(engine_type);
01773
01774
01775 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
01776 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01777
01778 if (callback != CALLBACK_FAILED) {
01779 assert_compile(PAL_NONE == 0);
01780 map = GB(callback, 0, 14);
01781
01782
01783 if (!HasBit(callback, 14)) {
01784
01785 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01786 return map;
01787 }
01788 }
01789 }
01790
01791 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
01792
01793 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
01794
01795
01796 if (!Company::IsValidID(company)) return map;
01797
01798 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
01799
01800 map += livery->colour1;
01801 if (twocc) map += livery->colour2 * 16;
01802
01803
01804 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01805 return map;
01806 }
01807
01814 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
01815 {
01816 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01817 }
01818
01824 PaletteID GetVehiclePalette(const Vehicle *v)
01825 {
01826 if (v->IsGroundVehicle()) {
01827 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
01828 }
01829
01830 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01831 }
01832
01836 void Vehicle::DeleteUnreachedImplicitOrders()
01837 {
01838 if (this->IsGroundVehicle()) {
01839 uint16 &gv_flags = this->GetGroundVehicleFlags();
01840 if (HasBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)) {
01841
01842 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
01843 this->cur_implicit_order_index = this->cur_real_order_index;
01844 InvalidateVehicleOrder(this, 0);
01845 return;
01846 }
01847 }
01848
01849 const Order *order = this->GetOrder(this->cur_implicit_order_index);
01850 while (order != NULL) {
01851 if (this->cur_implicit_order_index == this->cur_real_order_index) break;
01852
01853 if (order->IsType(OT_IMPLICIT)) {
01854
01855 order = order->next;
01856 DeleteOrder(this, this->cur_implicit_order_index);
01857 } else {
01858
01859 order = order->next;
01860 this->cur_implicit_order_index++;
01861 }
01862
01863
01864 if (order == NULL) {
01865 order = this->GetOrder(0);
01866 this->cur_implicit_order_index = 0;
01867 }
01868 }
01869 }
01870
01875 void Vehicle::BeginLoading()
01876 {
01877 assert(IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP);
01878
01879 if (this->current_order.IsType(OT_GOTO_STATION) &&
01880 this->current_order.GetDestination() == this->last_station_visited) {
01881 this->DeleteUnreachedImplicitOrders();
01882
01883
01884 this->current_order.MakeLoading(true);
01885 UpdateVehicleTimetable(this, true);
01886
01887
01888
01889
01890
01891
01892 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
01893
01894 } else {
01895
01896
01897
01898
01899
01900 Order *in_list = this->GetOrder(this->cur_implicit_order_index);
01901 if (this->IsGroundVehicle() && in_list != NULL &&
01902 (!in_list->IsType(OT_IMPLICIT) ||
01903 in_list->GetDestination() != this->last_station_visited)) {
01904 bool suppress_implicit_orders = HasBit(this->GetGroundVehicleFlags(), GVF_SUPPRESS_IMPLICIT_ORDERS);
01905
01906 Order *prev_order = this->cur_implicit_order_index > 0 ? this->GetOrder(this->cur_implicit_order_index - 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : NULL);
01907 if (prev_order == NULL ||
01908 (!prev_order->IsType(OT_IMPLICIT) && !prev_order->IsType(OT_GOTO_STATION)) ||
01909 prev_order->GetDestination() != this->last_station_visited) {
01910
01911
01912
01913 int target_index = this->cur_implicit_order_index;
01914 bool found = false;
01915 while (target_index != this->cur_real_order_index) {
01916 const Order *order = this->GetOrder(target_index);
01917 if (order->IsType(OT_IMPLICIT) && order->GetDestination() == this->last_station_visited) {
01918 found = true;
01919 break;
01920 }
01921 target_index++;
01922 if (target_index >= this->orders.list->GetNumOrders()) target_index = 0;
01923 assert(target_index != this->cur_implicit_order_index);
01924 }
01925
01926 if (found) {
01927 if (suppress_implicit_orders) {
01928
01929 this->cur_implicit_order_index = target_index;
01930 InvalidateVehicleOrder(this, 0);
01931 } else {
01932
01933 const Order *order = this->GetOrder(this->cur_implicit_order_index);
01934 while (!order->IsType(OT_IMPLICIT) || order->GetDestination() != this->last_station_visited) {
01935 if (order->IsType(OT_IMPLICIT)) {
01936
01937 order = order->next;
01938 DeleteOrder(this, this->cur_implicit_order_index);
01939 } else {
01940
01941 order = order->next;
01942 this->cur_implicit_order_index++;
01943 }
01944
01945
01946 if (order == NULL) {
01947 order = this->GetOrder(0);
01948 this->cur_implicit_order_index = 0;
01949 }
01950 assert(order != NULL);
01951 }
01952 }
01953 } else if (!suppress_implicit_orders && this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID && Order::CanAllocateItem()) {
01954
01955 Order *implicit_order = new Order();
01956 implicit_order->MakeImplicit(this->last_station_visited);
01957 InsertOrder(this, implicit_order, this->cur_implicit_order_index);
01958 if (this->cur_implicit_order_index > 0) --this->cur_implicit_order_index;
01959
01960
01961
01962 uint16 &gv_flags = this->GetGroundVehicleFlags();
01963 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
01964 }
01965 }
01966 }
01967 this->current_order.MakeLoading(false);
01968 }
01969
01970 if (this->last_loading_station != INVALID_STATION &&
01971 this->last_loading_station != this->last_station_visited &&
01972 ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
01973 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0)) {
01974 IncreaseStats(Station::Get(this->last_loading_station), this, this->last_station_visited);
01975 }
01976
01977 PrepareUnload(this);
01978
01979 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
01980 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
01981 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01982 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
01983
01984 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
01985 this->cur_speed = 0;
01986 this->MarkDirty();
01987 }
01988
01993 void Vehicle::CancelReservation(StationID next, Station *st)
01994 {
01995 for (Vehicle *v = this; v != NULL; v = v->next) {
01996 VehicleCargoList &cargo = v->cargo;
01997 if (cargo.ReservedCount() > 0) {
01998 DEBUG(misc, 1, "cancelling cargo reservation");
01999 GoodsEntry &ge = st->goods[v->cargo_type];
02000 cargo.Unreserve(next, &ge.cargo);
02001 SetBit(ge.acceptance_pickup, GoodsEntry::GES_PICKUP);
02002 }
02003 }
02004 }
02005
02010 void Vehicle::LeaveStation()
02011 {
02012 assert(this->current_order.IsType(OT_LOADING));
02013
02014 delete this->cargo_payment;
02015
02016
02017 if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
02018
02019 if ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
02020 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
02021 if (this->current_order.CanLeaveWithCargo(this->last_loading_station != INVALID_STATION)) {
02022
02023
02024
02025 this->RefreshNextHopsStats();
02026
02027
02028 this->last_loading_station = this->last_station_visited;
02029 } else {
02030
02031
02032 this->last_loading_station = INVALID_STATION;
02033 }
02034 }
02035
02036 this->current_order.MakeLeaveStation();
02037 Station *st = Station::Get(this->last_station_visited);
02038 this->CancelReservation(INVALID_STATION, st);
02039 st->loading_vehicles.remove(this);
02040
02041 HideFillingPercent(&this->fill_percent_te_id);
02042
02043 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
02044
02045 if (IsTileType(this->tile, MP_STATION)) TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
02046
02047 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
02048 }
02049 }
02050
02057 void Vehicle::RefreshNextHopsStats()
02058 {
02059
02060 SmallMap<CargoID, uint, 1> capacities;
02061 for (Vehicle *v = this; v != NULL; v = v->Next()) {
02062 v->refit_cap = v->cargo_cap;
02063 if (v->refit_cap == 0) continue;
02064
02065 SmallPair<CargoID, uint> *i = capacities.Find(v->cargo_type);
02066 if (i == capacities.End()) {
02067
02068
02069
02070
02071
02072 i = capacities.Append();
02073 i->first = v->cargo_type;
02074 i->second = v->refit_cap;
02075 } else {
02076 i->second += v->refit_cap;
02077 }
02078 }
02079
02080
02081 if (this->orders.list == NULL) return;
02082
02083 uint hops = 0;
02084 const Order *first = this->orders.list->GetNextStoppingOrder(this,
02085 this->GetOrder(this->cur_implicit_order_index), hops);
02086 const Order *cur = first;
02087 const Order *next = first;
02088 while (next != NULL && cur->CanLeaveWithCargo(true)) {
02089 next = this->orders.list->GetNextStoppingOrder(this,
02090 this->orders.list->GetNext(next), ++hops);
02091 if (next == NULL) break;
02092
02093 if (next->IsType(OT_GOTO_DEPOT)) {
02094
02095 CargoID new_cid = next->GetRefitCargo();
02096 byte new_subtype = next->GetRefitSubtype();
02097 for (Vehicle *v = this; v != NULL; v = v->Next()) {
02098 const Engine *e = Engine::Get(v->engine_type);
02099 if (!HasBit(e->info.refit_mask, new_cid)) continue;
02100
02101
02102 CargoID temp_cid = v->cargo_type;
02103 byte temp_subtype = v->cargo_subtype;
02104 v->cargo_type = new_cid;
02105 v->cargo_subtype = new_subtype;
02106
02107 uint16 mail_capacity = 0;
02108 uint amount = e->DetermineCapacity(v, &mail_capacity);
02109
02110
02111 v->cargo_type = temp_cid;
02112 v->cargo_subtype = temp_subtype;
02113
02114
02115 if (new_cid != v->cargo_type && v->refit_cap > 0) {
02116 capacities[v->cargo_type] -= v->refit_cap;
02117 v->refit_cap = 0;
02118 } else if (amount < v->refit_cap) {
02119 capacities[v->cargo_type] -= v->refit_cap - amount;
02120 v->refit_cap = amount;
02121 }
02122
02123
02124 if (v->type == VEH_AIRCRAFT) {
02125 Vehicle *u = v->Next();
02126 if (mail_capacity < u->refit_cap) {
02127 capacities[u->cargo_type] -= u->refit_cap - mail_capacity;
02128 u->refit_cap = mail_capacity;
02129 }
02130 break;
02131 }
02132 }
02133 } else {
02134 StationID next_station = next->GetDestination();
02135 Station *st = Station::GetIfValid(cur->GetDestination());
02136 if (st != NULL && next_station != INVALID_STATION && next_station != st->index) {
02137 for (const SmallPair<CargoID, uint> *i = capacities.Begin(); i != capacities.End(); ++i) {
02138
02139 if (i->second > 0) IncreaseStats(st, i->first, next_station, i->second, UINT_MAX);
02140 }
02141 }
02142 cur = next;
02143 if (cur == first) break;
02144 }
02145 }
02146
02147 for (Vehicle *v = this; v != NULL; v = v->Next()) v->refit_cap = v->cargo_cap;
02148 }
02149
02155 void Vehicle::HandleLoading(bool mode)
02156 {
02157 switch (this->current_order.GetType()) {
02158 case OT_LOADING: {
02159 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
02160
02161
02162 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
02163
02164 this->PlayLeaveStationSound();
02165
02166 this->LeaveStation();
02167
02168
02169 const Order *order = this->GetOrder(this->cur_implicit_order_index);
02170 if (order == NULL ||
02171 (!order->IsType(OT_IMPLICIT) && !order->IsType(OT_GOTO_STATION)) ||
02172 order->GetDestination() != this->last_station_visited) {
02173 return;
02174 }
02175 break;
02176 }
02177
02178 case OT_DUMMY: break;
02179
02180 default: return;
02181 }
02182
02183 this->IncrementImplicitOrderIndex();
02184 }
02185
02190 void Vehicle::GetConsistFreeCapacities(SmallMap<CargoID, uint> &capacities) const
02191 {
02192 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
02193 if (v->cargo_cap == 0) continue;
02194 SmallPair<CargoID, uint> *pair = capacities.Find(v->cargo_type);
02195 if (pair == capacities.End()) {
02196 pair = capacities.Append();
02197 pair->first = v->cargo_type;
02198 pair->second = v->cargo_cap - v->cargo.Count();
02199 } else {
02200 pair->second += v->cargo_cap - v->cargo.Count();
02201 }
02202 }
02203 }
02204
02205 uint Vehicle::GetConsistTotalCapacity() const
02206 {
02207 uint result = 0;
02208 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
02209 result += v->cargo_cap;
02210 }
02211 return result;
02212 }
02213
02220 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
02221 {
02222 CommandCost ret = CheckOwnership(this->owner);
02223 if (ret.Failed()) return ret;
02224
02225 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
02226 if (this->IsStoppedInDepot()) return CMD_ERROR;
02227
02228 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
02229 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
02230 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
02231
02232
02233
02234 if (flags & DC_EXEC) {
02235 this->current_order.SetDepotOrderType(ODTF_MANUAL);
02236 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
02237 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02238 }
02239 return CommandCost();
02240 }
02241
02242 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR;
02243 if (flags & DC_EXEC) {
02244
02245
02246 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
02247
02248 if (this->IsGroundVehicle()) {
02249 uint16 &gv_flags = this->GetGroundVehicleFlags();
02250 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02251 }
02252
02253 this->current_order.MakeDummy();
02254 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02255 }
02256 return CommandCost();
02257 }
02258
02259 TileIndex location;
02260 DestinationID destination;
02261 bool reverse;
02262 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};
02263 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
02264
02265 if (flags & DC_EXEC) {
02266 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
02267
02268 if (this->IsGroundVehicle()) {
02269 uint16 &gv_flags = this->GetGroundVehicleFlags();
02270 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02271 }
02272
02273 this->dest_tile = location;
02274 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
02275 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
02276 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02277
02278
02279 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
02280
02281 if (this->type == VEH_AIRCRAFT) {
02282 Aircraft *a = Aircraft::From(this);
02283 if (a->state == FLYING && a->targetairport != destination) {
02284
02285 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
02286 AircraftNextAirportPos_and_Order(a);
02287 }
02288 }
02289 }
02290
02291 return CommandCost();
02292
02293 }
02294
02299 void Vehicle::UpdateVisualEffect(bool allow_power_change)
02300 {
02301 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02302 const Engine *e = this->GetEngine();
02303
02304
02305 byte visual_effect;
02306 switch (e->type) {
02307 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
02308 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
02309 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
02310 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
02311 }
02312
02313
02314 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
02315 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
02316
02317 if (callback != CALLBACK_FAILED) {
02318 if (callback >= 0x100 && e->GetGRF()->grf_version >= 8) ErrorUnknownCallbackResult(e->GetGRFID(), CBID_VEHICLE_VISUAL_EFFECT, callback);
02319
02320 callback = GB(callback, 0, 8);
02321
02322
02323 if (callback == VE_DEFAULT) {
02324 assert(HasBit(callback, VE_DISABLE_EFFECT));
02325 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
02326 }
02327 visual_effect = callback;
02328 }
02329 }
02330
02331
02332 if (visual_effect == VE_DEFAULT ||
02333 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
02334
02335
02336 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
02337 if (visual_effect == VE_DEFAULT) {
02338 visual_effect = 1 << VE_DISABLE_EFFECT;
02339 } else {
02340 SetBit(visual_effect, VE_DISABLE_EFFECT);
02341 }
02342 } else {
02343 if (visual_effect == VE_DEFAULT) {
02344
02345 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
02346 }
02347 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
02348 }
02349 }
02350
02351 this->vcache.cached_vis_effect = visual_effect;
02352
02353 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
02354 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02355 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
02356 }
02357 }
02358
02359 static const int8 _vehicle_smoke_pos[8] = {
02360 1, 1, 1, 0, -1, -1, -1, 0
02361 };
02362
02367 void Vehicle::ShowVisualEffect() const
02368 {
02369 assert(this->IsPrimaryVehicle());
02370 bool sound = false;
02371
02372
02373
02374
02375
02376
02377 if (_settings_game.vehicle.smoke_amount == 0 ||
02378 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
02379 this->cur_speed < 2) {
02380 return;
02381 }
02382
02383 uint max_speed = this->vcache.cached_max_speed;
02384 if (this->type == VEH_TRAIN) {
02385 const Train *t = Train::From(this);
02386
02387
02388
02389
02390 if (HasBit(t->flags, VRF_REVERSING) ||
02391 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
02392 t->cur_speed >= t->Train::GetCurrentMaxSpeed())) {
02393 return;
02394 }
02395
02396 max_speed = min(max_speed, t->gcache.cached_max_track_speed);
02397 max_speed = min(max_speed, this->current_order.max_speed);
02398 }
02399 if (this->type == VEH_ROAD || this->type == VEH_SHIP) max_speed = min(max_speed, this->current_order.max_speed * 2);
02400
02401 const Vehicle *v = this;
02402
02403 do {
02404 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
02405 byte effect_type = GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
02406 bool disable_effect = HasBit(v->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
02407
02408
02409
02410
02411
02412
02413
02414
02415 if (disable_effect ||
02416 v->vehstatus & VS_HIDDEN ||
02417 (MayHaveBridgeAbove(v->tile) && IsBridgeAbove(v->tile)) ||
02418 IsDepotTile(v->tile) ||
02419 IsTunnelTile(v->tile) ||
02420 (v->type == VEH_TRAIN &&
02421 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
02422 continue;
02423 }
02424
02425
02426
02427
02428 if (v->type == VEH_TRAIN) effect_offset += (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
02429
02430 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
02431 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
02432
02433 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
02434 x = -x;
02435 y = -y;
02436 }
02437
02438 switch (effect_type) {
02439 case VE_TYPE_STEAM:
02440
02441
02442
02443
02444
02445 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / max_speed))) == 0) {
02446 CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
02447 sound = true;
02448 }
02449 break;
02450
02451 case VE_TYPE_DIESEL: {
02452
02453
02454
02455
02456
02457
02458
02459
02460
02461
02462
02463 int power_weight_effect = 0;
02464 if (v->type == VEH_TRAIN) {
02465 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
02466 }
02467 if (this->cur_speed < (max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
02468 Chance16((64 - ((this->cur_speed << 5) / max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
02469 CreateEffectVehicleRel(v, x, y, 10, EV_DIESEL_SMOKE);
02470 sound = true;
02471 }
02472 break;
02473 }
02474
02475 case VE_TYPE_ELECTRIC:
02476
02477
02478
02479
02480
02481
02482 if (GB(v->tick_counter, 0, 2) == 0 &&
02483 Chance16((6 - ((this->cur_speed << 2) / max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
02484 CreateEffectVehicleRel(v, x, y, 10, EV_ELECTRIC_SPARK);
02485 sound = true;
02486 }
02487 break;
02488
02489 default:
02490 break;
02491 }
02492 } while ((v = v->Next()) != NULL);
02493
02494 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
02495 }
02496
02501 void Vehicle::SetNext(Vehicle *next)
02502 {
02503 assert(this != next);
02504
02505 if (this->next != NULL) {
02506
02507 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02508 v->first = this->next;
02509 }
02510 this->next->previous = NULL;
02511 }
02512
02513 this->next = next;
02514
02515 if (this->next != NULL) {
02516
02517 if (this->next->previous != NULL) this->next->previous->next = NULL;
02518 this->next->previous = this;
02519 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02520 v->first = this->first;
02521 }
02522 }
02523 }
02524
02530 void Vehicle::AddToShared(Vehicle *shared_chain)
02531 {
02532 assert(this->previous_shared == NULL && this->next_shared == NULL);
02533
02534 if (shared_chain->orders.list == NULL) {
02535 assert(shared_chain->previous_shared == NULL);
02536 assert(shared_chain->next_shared == NULL);
02537 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
02538 }
02539
02540 this->next_shared = shared_chain->next_shared;
02541 this->previous_shared = shared_chain;
02542
02543 shared_chain->next_shared = this;
02544
02545 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
02546
02547 shared_chain->orders.list->AddVehicle(this);
02548 }
02549
02553 void Vehicle::RemoveFromShared()
02554 {
02555
02556
02557 bool were_first = (this->FirstShared() == this);
02558 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
02559
02560 this->orders.list->RemoveVehicle(this);
02561
02562 if (!were_first) {
02563
02564 this->previous_shared->next_shared = this->NextShared();
02565 }
02566
02567 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
02568
02569
02570 if (this->orders.list->GetNumVehicles() == 1) {
02571
02572 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
02573 InvalidateVehicleOrder(this->FirstShared(), 0);
02574 } else if (were_first) {
02575
02576
02577 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
02578 }
02579
02580 this->next_shared = NULL;
02581 this->previous_shared = NULL;
02582 }
02583
02584 void VehiclesYearlyLoop()
02585 {
02586 Vehicle *v;
02587 FOR_ALL_VEHICLES(v) {
02588 if (v->IsPrimaryVehicle()) {
02589
02590 Money profit = v->GetDisplayProfitThisYear();
02591 if (v->age >= 730 && profit < 0) {
02592 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
02593 SetDParam(0, v->index);
02594 SetDParam(1, profit);
02595 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_UNPROFITABLE, v->index);
02596 }
02597 AI::NewEvent(v->owner, new ScriptEventVehicleUnprofitable(v->index));
02598 }
02599
02600 v->profit_last_year = v->profit_this_year;
02601 v->profit_this_year = 0;
02602 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
02603 }
02604 }
02605 GroupStatistics::UpdateProfits();
02606 SetWindowClassesDirty(WC_TRAINS_LIST);
02607 SetWindowClassesDirty(WC_SHIPS_LIST);
02608 SetWindowClassesDirty(WC_ROADVEH_LIST);
02609 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
02610 }
02611
02612
02622 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
02623 {
02624 const Engine *e = Engine::GetIfValid(engine_type);
02625 assert(e != NULL);
02626
02627 switch (e->type) {
02628 case VEH_TRAIN:
02629 return (st->facilities & FACIL_TRAIN) != 0;
02630
02631 case VEH_ROAD:
02632
02633
02634
02635 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
02636
02637 case VEH_SHIP:
02638 return (st->facilities & FACIL_DOCK) != 0;
02639
02640 case VEH_AIRCRAFT:
02641 return (st->facilities & FACIL_AIRPORT) != 0 &&
02642 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
02643
02644 default:
02645 return false;
02646 }
02647 }
02648
02655 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
02656 {
02657 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
02658
02659 return CanVehicleUseStation(v->engine_type, st);
02660 }
02661
02667 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
02668 {
02669 assert(this->IsGroundVehicle());
02670 if (this->type == VEH_TRAIN) {
02671 return &Train::From(this)->gcache;
02672 } else {
02673 return &RoadVehicle::From(this)->gcache;
02674 }
02675 }
02676
02682 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
02683 {
02684 assert(this->IsGroundVehicle());
02685 if (this->type == VEH_TRAIN) {
02686 return &Train::From(this)->gcache;
02687 } else {
02688 return &RoadVehicle::From(this)->gcache;
02689 }
02690 }
02691
02697 uint16 &Vehicle::GetGroundVehicleFlags()
02698 {
02699 assert(this->IsGroundVehicle());
02700 if (this->type == VEH_TRAIN) {
02701 return Train::From(this)->gv_flags;
02702 } else {
02703 return RoadVehicle::From(this)->gv_flags;
02704 }
02705 }
02706
02712 const uint16 &Vehicle::GetGroundVehicleFlags() const
02713 {
02714 assert(this->IsGroundVehicle());
02715 if (this->type == VEH_TRAIN) {
02716 return Train::From(this)->gv_flags;
02717 } else {
02718 return RoadVehicle::From(this)->gv_flags;
02719 }
02720 }
02721
02730 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
02731 {
02732 if (v->type == VEH_TRAIN) {
02733 Train *u = Train::From(v);
02734
02735 u = u->GetFirstEnginePart();
02736
02737
02738 for (; u != NULL && num_vehicles > 0; num_vehicles--) {
02739 do {
02740
02741 set.Include(u->index);
02742
02743
02744 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
02745
02746 u = u->Next();
02747 } while (u != NULL && u->IsArticulatedPart());
02748 }
02749 }
02750 }