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