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