00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include "error.h"
00014 #include "roadveh.h"
00015 #include "ship.h"
00016 #include "spritecache.h"
00017 #include "timetable.h"
00018 #include "viewport_func.h"
00019 #include "news_func.h"
00020 #include "command_func.h"
00021 #include "company_func.h"
00022 #include "train.h"
00023 #include "aircraft.h"
00024 #include "newgrf_debug.h"
00025 #include "newgrf_sound.h"
00026 #include "newgrf_station.h"
00027 #include "group_gui.h"
00028 #include "strings_func.h"
00029 #include "zoom_func.h"
00030 #include "date_func.h"
00031 #include "vehicle_func.h"
00032 #include "autoreplace_func.h"
00033 #include "autoreplace_gui.h"
00034 #include "station_base.h"
00035 #include "ai/ai.hpp"
00036 #include "depot_func.h"
00037 #include "network/network.h"
00038 #include "core/pool_func.hpp"
00039 #include "economy_base.h"
00040 #include "articulated_vehicles.h"
00041 #include "roadstop_base.h"
00042 #include "core/random_func.hpp"
00043 #include "core/backup_type.hpp"
00044 #include "order_backup.h"
00045 #include "sound_func.h"
00046 #include "effectvehicle_func.h"
00047 #include "effectvehicle_base.h"
00048 #include "vehiclelist.h"
00049 #include "bridge_map.h"
00050 #include "tunnel_map.h"
00051 #include "depot_map.h"
00052 #include "gamelog.h"
00053
00054 #include "table/strings.h"
00055
00056 #define GEN_HASH(x, y) ((GB((y), 6 + ZOOM_LVL_SHIFT, 6) << 6) + GB((x), 7 + ZOOM_LVL_SHIFT, 6))
00057
00058 VehicleID _new_vehicle_id;
00059 uint16 _returned_refit_capacity;
00060 uint16 _returned_mail_refit_capacity;
00061
00062
00064 VehiclePool _vehicle_pool("Vehicle");
00065 INSTANTIATE_POOL_METHODS(Vehicle)
00066
00067
00073 bool Vehicle::NeedsAutorenewing(const Company *c, bool use_renew_setting) const
00074 {
00075
00076
00077
00078
00079 assert(c == Company::Get(this->owner));
00080
00081 if (use_renew_setting && !c->settings.engine_renew) return false;
00082 if (this->age - this->max_age < (c->settings.engine_renew_months * 30)) return false;
00083
00084
00085 if (this->type == VEH_TRAIN && !Train::From(this)->IsEngine()) return false;
00086
00087 return true;
00088 }
00089
00090 void VehicleServiceInDepot(Vehicle *v)
00091 {
00092 v->date_of_last_service = _date;
00093 v->breakdowns_since_last_service = 0;
00094 v->reliability = v->GetEngine()->reliability;
00095
00096 v->breakdown_chance /= 4;
00097 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
00098 }
00099
00106 bool Vehicle::NeedsServicing() const
00107 {
00108
00109
00110 if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
00111
00112
00113 const Company *c = Company::Get(this->owner);
00114 if (c->settings.vehicle.servint_ispercent ?
00115 (this->reliability >= this->GetEngine()->reliability * (100 - this->service_interval) / 100) :
00116 (this->date_of_last_service + this->service_interval >= _date)) {
00117 return false;
00118 }
00119
00120
00121
00122 if (!_settings_game.order.no_servicing_if_no_breakdowns ||
00123 _settings_game.difficulty.vehicle_breakdowns != 0) {
00124 return true;
00125 }
00126
00127
00128
00129
00130 bool pending_replace = false;
00131 Money needed_money = c->settings.engine_renew_money;
00132 if (needed_money > c->money) return false;
00133
00134 for (const Vehicle *v = this; v != NULL; v = (v->type == VEH_TRAIN) ? Train::From(v)->GetNextUnit() : NULL) {
00135 bool replace_when_old = false;
00136 EngineID new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id, &replace_when_old);
00137
00138
00139 if (new_engine == INVALID_ENGINE || !HasBit(Engine::Get(new_engine)->company_avail, v->owner)) continue;
00140
00141 if (replace_when_old && !v->NeedsAutorenewing(c, false)) continue;
00142
00143
00144 uint32 available_cargo_types, union_mask;
00145 GetArticulatedRefitMasks(new_engine, true, &union_mask, &available_cargo_types);
00146
00147 if (union_mask != 0) {
00148 CargoID cargo_type;
00149
00150 if (IsArticulatedVehicleCarryingDifferentCargoes(v, &cargo_type)) continue;
00151
00152
00153 if (cargo_type != CT_INVALID) {
00154
00155 if (!HasBit(available_cargo_types, cargo_type)) continue;
00156 }
00157 }
00158
00159
00160
00161 pending_replace = true;
00162 needed_money += 2 * Engine::Get(new_engine)->GetCost();
00163 if (needed_money > c->money) return false;
00164 }
00165
00166 return pending_replace;
00167 }
00168
00174 bool Vehicle::NeedsAutomaticServicing() const
00175 {
00176 if (this->HasDepotOrder()) return false;
00177 if (this->current_order.IsType(OT_LOADING)) return false;
00178 if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
00179 return NeedsServicing();
00180 }
00181
00182 uint Vehicle::Crash(bool flooded)
00183 {
00184 assert((this->vehstatus & VS_CRASHED) == 0);
00185 assert(this->Previous() == NULL);
00186
00187 uint pass = 0;
00188
00189 if (this->IsPrimaryVehicle()) this->vehstatus |= VS_STOPPED;
00190
00191 for (Vehicle *v = this; v != NULL; v = v->Next()) {
00192 if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo.Count();
00193 v->vehstatus |= VS_CRASHED;
00194 MarkSingleVehicleDirty(v);
00195 }
00196
00197
00198 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00199 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
00200 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
00201 SetWindowDirty(WC_VEHICLE_DEPOT, this->tile);
00202
00203 delete this->cargo_payment;
00204 this->cargo_payment = NULL;
00205
00206 return RandomRange(pass + 1);
00207 }
00208
00209
00218 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
00219 {
00220 const Engine *e = Engine::Get(engine);
00221 GRFConfig *grfconfig = GetGRFConfig(e->GetGRFID());
00222
00223 if (!HasBit(grfconfig->grf_bugs, bug_type)) {
00224 SetBit(grfconfig->grf_bugs, bug_type);
00225 SetDParamStr(0, grfconfig->GetName());
00226 SetDParam(1, engine);
00227 ShowErrorMessage(part1, part2, WL_CRITICAL);
00228 if (!_networking) DoCommand(0, critical ? PM_PAUSED_ERROR : PM_PAUSED_NORMAL, 1, DC_EXEC, CMD_PAUSE);
00229 }
00230
00231
00232 char buffer[512];
00233
00234 SetDParamStr(0, grfconfig->GetName());
00235 GetString(buffer, part1, lastof(buffer));
00236 DEBUG(grf, 0, "%s", buffer + 3);
00237
00238 SetDParam(1, engine);
00239 GetString(buffer, part2, lastof(buffer));
00240 DEBUG(grf, 0, "%s", buffer + 3);
00241 }
00242
00248 void VehicleLengthChanged(const Vehicle *u)
00249 {
00250
00251 const Engine *engine = u->GetEngine();
00252 uint32 grfid = engine->grf_prop.grffile->grfid;
00253 GRFConfig *grfconfig = GetGRFConfig(grfid);
00254 if (GamelogGRFBugReverse(grfid, engine->grf_prop.local_id) || !HasBit(grfconfig->grf_bugs, GBUG_VEH_LENGTH)) {
00255 ShowNewGrfVehicleError(u->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_VEHICLE_LENGTH, GBUG_VEH_LENGTH, true);
00256 }
00257 }
00258
00263 Vehicle::Vehicle(VehicleType type)
00264 {
00265 this->type = type;
00266 this->coord.left = INVALID_COORD;
00267 this->group_id = DEFAULT_GROUP;
00268 this->fill_percent_te_id = INVALID_TE_ID;
00269 this->first = this;
00270 this->colourmap = PAL_NONE;
00271 this->cargo_age_counter = 1;
00272 }
00273
00278 byte VehicleRandomBits()
00279 {
00280 return GB(Random(), 0, 8);
00281 }
00282
00283
00284
00285 const int HASH_BITS = 7;
00286 const int HASH_SIZE = 1 << HASH_BITS;
00287 const int HASH_MASK = HASH_SIZE - 1;
00288 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
00289 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
00290
00291
00292
00293 const int HASH_RES = 0;
00294
00295 static Vehicle *_vehicle_tile_hash[TOTAL_HASH_SIZE];
00296
00297 static Vehicle *VehicleFromTileHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
00298 {
00299 for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
00300 for (int x = xl; ; x = (x + 1) & HASH_MASK) {
00301 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
00302 for (; v != NULL; v = v->hash_tile_next) {
00303 Vehicle *a = proc(v, data);
00304 if (find_first && a != NULL) return a;
00305 }
00306 if (x == xu) break;
00307 }
00308 if (y == yu) break;
00309 }
00310
00311 return NULL;
00312 }
00313
00314
00326 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
00327 {
00328 const int COLL_DIST = 6;
00329
00330
00331 int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00332 int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00333 int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00334 int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00335
00336 return VehicleFromTileHash(xl, yl, xu, yu, data, proc, find_first);
00337 }
00338
00353 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00354 {
00355 VehicleFromPosXY(x, y, data, proc, false);
00356 }
00357
00369 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00370 {
00371 return VehicleFromPosXY(x, y, data, proc, true) != NULL;
00372 }
00373
00384 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
00385 {
00386 int x = GB(TileX(tile), HASH_RES, HASH_BITS);
00387 int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
00388
00389 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
00390 for (; v != NULL; v = v->hash_tile_next) {
00391 if (v->tile != tile) continue;
00392
00393 Vehicle *a = proc(v, data);
00394 if (find_first && a != NULL) return a;
00395 }
00396
00397 return NULL;
00398 }
00399
00413 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00414 {
00415 VehicleFromPos(tile, data, proc, false);
00416 }
00417
00428 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00429 {
00430 return VehicleFromPos(tile, data, proc, true) != NULL;
00431 }
00432
00439 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
00440 {
00441 int z = *(int*)data;
00442
00443 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
00444 if (v->z_pos > z) return NULL;
00445
00446 return v;
00447 }
00448
00454 CommandCost EnsureNoVehicleOnGround(TileIndex tile)
00455 {
00456 int z = GetTileMaxPixelZ(tile);
00457
00458
00459
00460
00461
00462 Vehicle *v = VehicleFromPos(tile, &z, &EnsureNoVehicleProcZ, true);
00463 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00464 return CommandCost();
00465 }
00466
00468 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
00469 {
00470 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
00471 if (v == (const Vehicle *)data) return NULL;
00472
00473 return v;
00474 }
00475
00483 CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
00484 {
00485
00486
00487
00488
00489 Vehicle *v = VehicleFromPos(tile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
00490 if (v == NULL) v = VehicleFromPos(endtile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
00491
00492 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00493 return CommandCost();
00494 }
00495
00496 static Vehicle *EnsureNoTrainOnTrackProc(Vehicle *v, void *data)
00497 {
00498 TrackBits rail_bits = *(TrackBits *)data;
00499
00500 if (v->type != VEH_TRAIN) return NULL;
00501
00502 Train *t = Train::From(v);
00503 if ((t->track != rail_bits) && !TracksOverlap(t->track | rail_bits)) return NULL;
00504
00505 return v;
00506 }
00507
00516 CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
00517 {
00518
00519
00520
00521
00522 Vehicle *v = VehicleFromPos(tile, &track_bits, &EnsureNoTrainOnTrackProc, true);
00523 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
00524 return CommandCost();
00525 }
00526
00527 static void UpdateVehicleTileHash(Vehicle *v, bool remove)
00528 {
00529 Vehicle **old_hash = v->hash_tile_current;
00530 Vehicle **new_hash;
00531
00532 if (remove) {
00533 new_hash = NULL;
00534 } else {
00535 int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
00536 int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
00537 new_hash = &_vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
00538 }
00539
00540 if (old_hash == new_hash) return;
00541
00542
00543 if (old_hash != NULL) {
00544 if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = v->hash_tile_prev;
00545 *v->hash_tile_prev = v->hash_tile_next;
00546 }
00547
00548
00549 if (new_hash != NULL) {
00550 v->hash_tile_next = *new_hash;
00551 if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = &v->hash_tile_next;
00552 v->hash_tile_prev = new_hash;
00553 *new_hash = v;
00554 }
00555
00556
00557 v->hash_tile_current = new_hash;
00558 }
00559
00560 static Vehicle *_vehicle_viewport_hash[0x1000];
00561
00562 static void UpdateVehicleViewportHash(Vehicle *v, int x, int y)
00563 {
00564 Vehicle **old_hash, **new_hash;
00565 int old_x = v->coord.left;
00566 int old_y = v->coord.top;
00567
00568 new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(x, y)];
00569 old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(old_x, old_y)];
00570
00571 if (old_hash == new_hash) return;
00572
00573
00574 if (old_hash != NULL) {
00575 if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = v->hash_viewport_prev;
00576 *v->hash_viewport_prev = v->hash_viewport_next;
00577 }
00578
00579
00580 if (new_hash != NULL) {
00581 v->hash_viewport_next = *new_hash;
00582 if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = &v->hash_viewport_next;
00583 v->hash_viewport_prev = new_hash;
00584 *new_hash = v;
00585 }
00586 }
00587
00588 void ResetVehicleHash()
00589 {
00590 Vehicle *v;
00591 FOR_ALL_VEHICLES(v) { v->hash_tile_current = NULL; }
00592 memset(_vehicle_viewport_hash, 0, sizeof(_vehicle_viewport_hash));
00593 memset(_vehicle_tile_hash, 0, sizeof(_vehicle_tile_hash));
00594 }
00595
00596 void ResetVehicleColourMap()
00597 {
00598 Vehicle *v;
00599 FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
00600 }
00601
00606 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
00607 static AutoreplaceMap _vehicles_to_autoreplace;
00608
00609 void InitializeVehicles()
00610 {
00611 _vehicles_to_autoreplace.Reset();
00612 ResetVehicleHash();
00613 }
00614
00615 uint CountVehiclesInChain(const Vehicle *v)
00616 {
00617 uint count = 0;
00618 do count++; while ((v = v->Next()) != NULL);
00619 return count;
00620 }
00621
00626 bool Vehicle::IsEngineCountable() const
00627 {
00628 switch (this->type) {
00629 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
00630 case VEH_TRAIN:
00631 return !this->IsArticulatedPart() &&
00632 !Train::From(this)->IsRearDualheaded();
00633 case VEH_ROAD: return RoadVehicle::From(this)->IsFrontEngine();
00634 case VEH_SHIP: return true;
00635 default: return false;
00636 }
00637 }
00638
00643 bool Vehicle::HasEngineType() const
00644 {
00645 switch (this->type) {
00646 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
00647 case VEH_TRAIN:
00648 case VEH_ROAD:
00649 case VEH_SHIP: return true;
00650 default: return false;
00651 }
00652 }
00653
00659 const Engine *Vehicle::GetEngine() const
00660 {
00661 return Engine::Get(this->engine_type);
00662 }
00663
00669 const GRFFile *Vehicle::GetGRF() const
00670 {
00671 return this->GetEngine()->GetGRF();
00672 }
00673
00679 uint32 Vehicle::GetGRFID() const
00680 {
00681 return this->GetEngine()->GetGRFID();
00682 }
00683
00691 void Vehicle::HandlePathfindingResult(bool path_found)
00692 {
00693 if (path_found) {
00694
00695 if (!HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00696
00697
00698 ClrBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00699
00700 DeleteVehicleNews(this->index, STR_NEWS_VEHICLE_IS_LOST);
00701 return;
00702 }
00703
00704
00705 if (HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
00706
00707
00708 SetBit(this->vehicle_flags, VF_PATHFINDER_LOST);
00709
00710 AI::NewEvent(this->owner, new ScriptEventVehicleLost(this->index));
00711 if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) {
00712 SetDParam(0, this->index);
00713 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_LOST, this->index);
00714 }
00715 }
00716
00718 void Vehicle::PreDestructor()
00719 {
00720 if (CleaningPool()) return;
00721
00722 if (Station::IsValidID(this->last_station_visited)) {
00723 Station *st = Station::Get(this->last_station_visited);
00724 st->loading_vehicles.remove(this);
00725
00726 HideFillingPercent(&this->fill_percent_te_id);
00727 this->CancelReservation(INVALID_STATION, st);
00728 delete this->cargo_payment;
00729 }
00730
00731 if (this->IsEngineCountable()) {
00732 GroupStatistics::CountEngine(this, -1);
00733 if (this->IsPrimaryVehicle()) GroupStatistics::CountVehicle(this, -1);
00734 GroupStatistics::UpdateAutoreplace(this->owner);
00735
00736 if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
00737 DeleteGroupHighlightOfVehicle(this);
00738 }
00739
00740 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
00741 Aircraft *a = Aircraft::From(this);
00742 Station *st = GetTargetAirportIfValid(a);
00743 if (st != NULL) {
00744 const AirportFTA *layout = st->airport.GetFTA()->layout;
00745 CLRBITS(st->airport.flags, layout[a->previous_pos].block | layout[a->pos].block);
00746 }
00747 }
00748
00749
00750 if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
00751 RoadVehicle *v = RoadVehicle::From(this);
00752 if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
00753
00754 RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
00755 }
00756 }
00757
00758 if (this->Previous() == NULL) {
00759 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
00760 }
00761
00762 if (this->IsPrimaryVehicle()) {
00763 DeleteWindowById(WC_VEHICLE_VIEW, this->index);
00764 DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
00765 DeleteWindowById(WC_VEHICLE_REFIT, this->index);
00766 DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
00767 DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
00768 SetWindowDirty(WC_COMPANY, this->owner);
00769 OrderBackup::ClearVehicle(this);
00770 }
00771 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00772
00773 this->cargo.Truncate();
00774 DeleteVehicleOrders(this);
00775 DeleteDepotHighlightOfVehicle(this);
00776
00777 extern void StopGlobalFollowVehicle(const Vehicle *v);
00778 StopGlobalFollowVehicle(this);
00779
00780 ReleaseDisastersTargetingVehicle(this->index);
00781 }
00782
00783 Vehicle::~Vehicle()
00784 {
00785 if (CleaningPool()) {
00786 this->cargo.OnCleanPool();
00787 return;
00788 }
00789
00790
00791
00792 if (!(this->vehstatus & VS_HIDDEN)) MarkSingleVehicleDirty(this);
00793
00794 Vehicle *v = this->Next();
00795 this->SetNext(NULL);
00796
00797 delete v;
00798
00799 UpdateVehicleTileHash(this, true);
00800 UpdateVehicleViewportHash(this, INVALID_COORD, 0);
00801 DeleteVehicleNews(this->index, INVALID_STRING_ID);
00802 DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index);
00803 }
00804
00809 void VehicleEnteredDepotThisTick(Vehicle *v)
00810 {
00811
00812 _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
00813
00814
00815
00816
00817
00818
00819 v->vehstatus |= VS_STOPPED;
00820 }
00821
00827 static void RunVehicleDayProc()
00828 {
00829 if (_game_mode != GM_NORMAL) return;
00830
00831
00832 for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
00833 Vehicle *v = Vehicle::Get(i);
00834 if (v == NULL) continue;
00835
00836
00837 if ((v->day_counter & 0x1F) == 0 && v->HasEngineType()) {
00838 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
00839 if (callback != CALLBACK_FAILED) {
00840 if (HasBit(callback, 0)) {
00841
00842 TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32);
00843 v->MarkDirty();
00844 }
00845 if (HasBit(callback, 1)) v->colourmap = PAL_NONE;
00846
00847 if (callback & ~3) ErrorUnknownCallbackResult(v->GetGRFID(), CBID_VEHICLE_32DAY_CALLBACK, callback);
00848 }
00849 }
00850
00851
00852 v->OnNewDay();
00853 }
00854 }
00855
00856 void CallVehicleTicks()
00857 {
00858 _vehicles_to_autoreplace.Clear();
00859
00860 RunVehicleDayProc();
00861
00862 Station *st;
00863 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
00864
00865 Vehicle *v;
00866 FOR_ALL_VEHICLES(v) {
00867
00868 if (!v->Tick()) {
00869 assert(Vehicle::Get(vehicle_index) == NULL);
00870 continue;
00871 }
00872
00873 assert(Vehicle::Get(vehicle_index) == v);
00874
00875 switch (v->type) {
00876 default: break;
00877
00878 case VEH_TRAIN:
00879 case VEH_ROAD:
00880 case VEH_AIRCRAFT:
00881 case VEH_SHIP:
00882 if (v->vcache.cached_cargo_age_period != 0) {
00883 v->cargo_age_counter = min(v->cargo_age_counter, v->vcache.cached_cargo_age_period);
00884 if (--v->cargo_age_counter == 0) {
00885 v->cargo.AgeCargo();
00886 v->cargo_age_counter = v->vcache.cached_cargo_age_period;
00887 }
00888 }
00889
00890 if (v->type == VEH_TRAIN && Train::From(v)->IsWagon()) continue;
00891 if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
00892 if (v->type == VEH_ROAD && !RoadVehicle::From(v)->IsFrontEngine()) continue;
00893
00894 v->motion_counter += v->cur_speed;
00895
00896 if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
00897
00898
00899 if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
00900 }
00901 }
00902
00903 Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
00904 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
00905 v = it->first;
00906
00907 cur_company.Change(v->owner);
00908
00909
00910
00911
00912 if (it->second) v->vehstatus &= ~VS_STOPPED;
00913
00914
00915 int x = v->x_pos;
00916 int y = v->y_pos;
00917 int z = v->z_pos;
00918
00919 const Company *c = Company::Get(_current_company);
00920 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
00921 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
00922 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
00923
00924 if (!IsLocalCompany()) continue;
00925
00926 if (res.Succeeded()) {
00927 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
00928 continue;
00929 }
00930
00931 StringID error_message = res.GetErrorMessage();
00932 if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
00933
00934 if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
00935
00936 StringID message;
00937 if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
00938 message = error_message;
00939 } else {
00940 message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
00941 }
00942
00943 SetDParam(0, v->index);
00944 SetDParam(1, error_message);
00945 AddVehicleAdviceNewsItem(message, v->index);
00946 }
00947
00948 cur_company.Restore();
00949 }
00950
00955 static void DoDrawVehicle(const Vehicle *v)
00956 {
00957 SpriteID image = v->cur_image;
00958 PaletteID pal = PAL_NONE;
00959
00960 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
00961
00962
00963 bool shadowed = (v->vehstatus & VS_SHADOW) != 0;
00964
00965 if (v->type == VEH_EFFECT) {
00966
00967
00968 TransparencyOption to = EffectVehicle::From(v)->GetTransparencyOption();
00969 if (to != TO_INVALID && (IsTransparencySet(to) || IsInvisibilitySet(to))) return;
00970 }
00971
00972 AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
00973 v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs);
00974 }
00975
00980 void ViewportAddVehicles(DrawPixelInfo *dpi)
00981 {
00982
00983 const int l = dpi->left;
00984 const int r = dpi->left + dpi->width;
00985 const int t = dpi->top;
00986 const int b = dpi->top + dpi->height;
00987
00988
00989 int xl, xu, yl, yu;
00990
00991 if (dpi->width + (70 * ZOOM_LVL_BASE) < (1 << (7 + 6 + ZOOM_LVL_SHIFT))) {
00992 xl = GB(l - (70 * ZOOM_LVL_BASE), 7 + ZOOM_LVL_SHIFT, 6);
00993 xu = GB(r, 7 + ZOOM_LVL_SHIFT, 6);
00994 } else {
00995
00996 xl = 0;
00997 xu = 0x3F;
00998 }
00999
01000 if (dpi->height + (70 * ZOOM_LVL_BASE) < (1 << (6 + 6 + ZOOM_LVL_SHIFT))) {
01001 yl = GB(t - (70 * ZOOM_LVL_BASE), 6 + ZOOM_LVL_SHIFT, 6) << 6;
01002 yu = GB(b, 6 + ZOOM_LVL_SHIFT, 6) << 6;
01003 } else {
01004
01005 yl = 0;
01006 yu = 0x3F << 6;
01007 }
01008
01009 for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
01010 for (int x = xl;; x = (x + 1) & 0x3F) {
01011 const Vehicle *v = _vehicle_viewport_hash[x + y];
01012
01013 while (v != NULL) {
01014 if (!(v->vehstatus & VS_HIDDEN) &&
01015 l <= v->coord.right &&
01016 t <= v->coord.bottom &&
01017 r >= v->coord.left &&
01018 b >= v->coord.top) {
01019 DoDrawVehicle(v);
01020 }
01021 v = v->hash_viewport_next;
01022 }
01023
01024 if (x == xu) break;
01025 }
01026
01027 if (y == yu) break;
01028 }
01029 }
01030
01038 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
01039 {
01040 Vehicle *found = NULL, *v;
01041 uint dist, best_dist = UINT_MAX;
01042
01043 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
01044
01045 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
01046 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
01047
01048 FOR_ALL_VEHICLES(v) {
01049 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
01050 x >= v->coord.left && x <= v->coord.right &&
01051 y >= v->coord.top && y <= v->coord.bottom) {
01052
01053 dist = max(
01054 abs(((v->coord.left + v->coord.right) >> 1) - x),
01055 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
01056 );
01057
01058 if (dist < best_dist) {
01059 found = v;
01060 best_dist = dist;
01061 }
01062 }
01063 }
01064
01065 return found;
01066 }
01067
01072 void DecreaseVehicleValue(Vehicle *v)
01073 {
01074 v->value -= v->value >> 8;
01075 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01076 }
01077
01078 static const byte _breakdown_chance[64] = {
01079 3, 3, 3, 3, 3, 3, 3, 3,
01080 4, 4, 5, 5, 6, 6, 7, 7,
01081 8, 8, 9, 9, 10, 10, 11, 11,
01082 12, 13, 13, 13, 13, 14, 15, 16,
01083 17, 19, 21, 25, 28, 31, 34, 37,
01084 40, 44, 48, 52, 56, 60, 64, 68,
01085 72, 80, 90, 100, 110, 120, 130, 140,
01086 150, 170, 190, 210, 230, 250, 250, 250,
01087 };
01088
01089 void CheckVehicleBreakdown(Vehicle *v)
01090 {
01091 int rel, rel_old;
01092
01093
01094 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
01095 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01096
01097 if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
01098 _settings_game.difficulty.vehicle_breakdowns < 1 ||
01099 v->cur_speed < 5 || _game_mode == GM_MENU) {
01100 return;
01101 }
01102
01103 uint32 r = Random();
01104
01105
01106 int chance = v->breakdown_chance + 1;
01107 if (Chance16I(1, 25, r)) chance += 25;
01108 v->breakdown_chance = min(255, chance);
01109
01110
01111 rel = v->reliability;
01112 if (v->type == VEH_SHIP) rel += 0x6666;
01113
01114
01115 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
01116
01117
01118 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
01119 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
01120 v->breakdown_delay = GB(r, 24, 7) + 0x80;
01121 v->breakdown_chance = 0;
01122 }
01123 }
01124
01131 bool Vehicle::HandleBreakdown()
01132 {
01133
01134
01135
01136
01137
01138 switch (this->breakdown_ctr) {
01139 case 0:
01140 return false;
01141
01142 case 2:
01143 this->breakdown_ctr = 1;
01144
01145 if (this->breakdowns_since_last_service != 255) {
01146 this->breakdowns_since_last_service++;
01147 }
01148
01149 if (this->type == VEH_AIRCRAFT) {
01150
01151 this->vehstatus |= VS_AIRCRAFT_BROKEN;
01152 } else {
01153 this->cur_speed = 0;
01154
01155 if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
01156 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
01157 (this->type == VEH_TRAIN ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
01158 (this->type == VEH_TRAIN ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
01159 }
01160
01161 if (!(this->vehstatus & VS_HIDDEN) && !HasBit(EngInfo(this->engine_type)->misc_flags, EF_NO_BREAKDOWN_SMOKE)) {
01162 EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
01163 if (u != NULL) u->animation_state = this->breakdown_delay * 2;
01164 }
01165 }
01166
01167 this->MarkDirty();
01168 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01169 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
01170
01171
01172 case 1:
01173
01174 if (this->type == VEH_AIRCRAFT) return false;
01175
01176
01177 if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
01178 if (--this->breakdown_delay == 0) {
01179 this->breakdown_ctr = 0;
01180 this->MarkDirty();
01181 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
01182 }
01183 }
01184 return true;
01185
01186 default:
01187 if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
01188 return false;
01189 }
01190 }
01191
01196 void AgeVehicle(Vehicle *v)
01197 {
01198 if (v->age < MAX_DAY) {
01199 v->age++;
01200 if (v->IsPrimaryVehicle() && v->age == VEHICLE_PROFIT_MIN_AGE + 1) GroupStatistics::VehicleReachedProfitAge(v);
01201 }
01202
01203 if (!v->IsPrimaryVehicle() && (v->type != VEH_TRAIN || !Train::From(v)->IsEngine())) return;
01204
01205 int age = v->age - v->max_age;
01206 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
01207 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
01208 v->reliability_spd_dec <<= 1;
01209 }
01210
01211 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01212
01213
01214 if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
01215
01216
01217 if (Company::Get(v->owner)->settings.engine_renew && v->GetEngine()->company_avail != 0) return;
01218
01219 StringID str;
01220 if (age == -DAYS_IN_LEAP_YEAR) {
01221 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
01222 } else if (age == 0) {
01223 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
01224 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
01225 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
01226 } else {
01227 return;
01228 }
01229
01230 SetDParam(0, v->index);
01231 AddVehicleAdviceNewsItem(str, v->index);
01232 }
01233
01240 uint8 CalcPercentVehicleFilled(const Vehicle *front, StringID *colour)
01241 {
01242 int count = 0;
01243 int max = 0;
01244 int cars = 0;
01245 int unloading = 0;
01246 bool loading = false;
01247
01248 bool is_loading = front->current_order.IsType(OT_LOADING);
01249
01250
01251 const Station *st = Station::GetIfValid(front->last_station_visited);
01252 assert(colour == NULL || (st != NULL && is_loading));
01253
01254 bool order_no_load = is_loading && (front->current_order.GetLoadType() & OLFB_NO_LOAD);
01255 bool order_full_load = is_loading && (front->current_order.GetLoadType() & OLFB_FULL_LOAD);
01256
01257
01258 for (const Vehicle *v = front; v != NULL; v = v->Next()) {
01259 count += v->cargo.OnboardCount();
01260 max += v->cargo_cap;
01261 if (v->cargo_cap != 0 && colour != NULL) {
01262 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
01263 loading |= !order_no_load &&
01264 (order_full_load || HasBit(st->goods[v->cargo_type].acceptance_pickup, GoodsEntry::GES_PICKUP)) &&
01265 !HasBit(v->vehicle_flags, VF_LOADING_FINISHED) && !HasBit(v->vehicle_flags, VF_STOP_LOADING);
01266 cars++;
01267 }
01268 }
01269
01270 if (colour != NULL) {
01271 if (unloading == 0 && loading) {
01272 *colour = STR_PERCENT_UP;
01273 } else if (unloading == 0 && !loading) {
01274 *colour = STR_PERCENT_NONE;
01275 } else if (cars == unloading || !loading) {
01276 *colour = STR_PERCENT_DOWN;
01277 } else {
01278 *colour = STR_PERCENT_UP_DOWN;
01279 }
01280 }
01281
01282
01283 if (max == 0) return 100;
01284
01285
01286 return (count * 100) / max;
01287 }
01288
01293 void VehicleEnterDepot(Vehicle *v)
01294 {
01295
01296 assert(v == v->First());
01297
01298 switch (v->type) {
01299 case VEH_TRAIN: {
01300 Train *t = Train::From(v);
01301 SetWindowClassesDirty(WC_TRAINS_LIST);
01302
01303 SetDepotReservation(t->tile, false);
01304 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
01305
01306 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
01307 t->wait_counter = 0;
01308 t->force_proceed = TFP_NONE;
01309 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
01310 t->ConsistChanged(true);
01311 break;
01312 }
01313
01314 case VEH_ROAD:
01315 SetWindowClassesDirty(WC_ROADVEH_LIST);
01316 break;
01317
01318 case VEH_SHIP: {
01319 SetWindowClassesDirty(WC_SHIPS_LIST);
01320 Ship *ship = Ship::From(v);
01321 ship->state = TRACK_BIT_DEPOT;
01322 ship->UpdateCache();
01323 ship->UpdateViewport(true, true);
01324 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01325 break;
01326 }
01327
01328 case VEH_AIRCRAFT:
01329 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01330 HandleAircraftEnterHangar(Aircraft::From(v));
01331 break;
01332 default: NOT_REACHED();
01333 }
01334 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01335
01336 if (v->type != VEH_TRAIN) {
01337
01338
01339 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01340 }
01341 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
01342
01343 v->vehstatus |= VS_HIDDEN;
01344 v->cur_speed = 0;
01345
01346 VehicleServiceInDepot(v);
01347
01348
01349 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01350 v->MarkDirty();
01351
01352 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01353 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01354
01355 const Order *real_order = v->GetOrder(v->cur_real_order_index);
01356 Order t = v->current_order;
01357 v->current_order.MakeDummy();
01358
01359
01360
01361 if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
01362 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
01363 (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
01364
01365 return;
01366 }
01367
01368 if (t.IsRefit()) {
01369 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01370 CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
01371 cur_company.Restore();
01372
01373 if (cost.Failed()) {
01374 _vehicles_to_autoreplace[v] = false;
01375 if (v->owner == _local_company) {
01376
01377 SetDParam(0, v->index);
01378 AddVehicleAdviceNewsItem(STR_NEWS_ORDER_REFIT_FAILED, v->index);
01379 }
01380 } else if (cost.GetCost() != 0) {
01381 v->profit_this_year -= cost.GetCost() << 8;
01382 if (v->owner == _local_company) {
01383 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01384 }
01385 }
01386 }
01387
01388 if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01389
01390 v->DeleteUnreachedImplicitOrders();
01391 UpdateVehicleTimetable(v, true);
01392 v->IncrementImplicitOrderIndex();
01393 }
01394 if (t.GetDepotActionType() & ODATFB_HALT) {
01395
01396 _vehicles_to_autoreplace[v] = false;
01397 if (v->owner == _local_company) {
01398 SetDParam(0, v->index);
01399 AddVehicleAdviceNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, v->index);
01400 }
01401 AI::NewEvent(v->owner, new ScriptEventVehicleWaitingInDepot(v->index));
01402 }
01403 }
01404 }
01405
01406
01412 void VehicleUpdatePosition(Vehicle *v)
01413 {
01414 UpdateVehicleTileHash(v, false);
01415 }
01416
01423 void VehicleUpdateViewport(Vehicle *v, bool dirty)
01424 {
01425 int img = v->cur_image;
01426 Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
01427 const Sprite *spr = GetSprite(img, ST_NORMAL);
01428
01429 pt.x += spr->x_offs;
01430 pt.y += spr->y_offs;
01431
01432 UpdateVehicleViewportHash(v, pt.x, pt.y);
01433
01434 Rect old_coord = v->coord;
01435 v->coord.left = pt.x;
01436 v->coord.top = pt.y;
01437 v->coord.right = pt.x + spr->width + 2 * ZOOM_LVL_BASE;
01438 v->coord.bottom = pt.y + spr->height + 2 * ZOOM_LVL_BASE;
01439
01440 if (dirty) {
01441 if (old_coord.left == INVALID_COORD) {
01442 MarkSingleVehicleDirty(v);
01443 } else {
01444 MarkAllViewportsDirty(
01445 min(old_coord.left, v->coord.left),
01446 min(old_coord.top, v->coord.top),
01447 max(old_coord.right, v->coord.right) + 1 * ZOOM_LVL_BASE,
01448 max(old_coord.bottom, v->coord.bottom) + 1 * ZOOM_LVL_BASE
01449 );
01450 }
01451 }
01452 }
01453
01458 void VehicleUpdatePositionAndViewport(Vehicle *v)
01459 {
01460 VehicleUpdatePosition(v);
01461 VehicleUpdateViewport(v, true);
01462 }
01463
01468 void MarkSingleVehicleDirty(const Vehicle *v)
01469 {
01470 MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1 * ZOOM_LVL_BASE, v->coord.bottom + 1 * ZOOM_LVL_BASE);
01471 }
01472
01478 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01479 {
01480 static const int8 _delta_coord[16] = {
01481 -1,-1,-1, 0, 1, 1, 1, 0,
01482 -1, 0, 1, 1, 1, 0,-1,-1,
01483 };
01484
01485 int x = v->x_pos + _delta_coord[v->direction];
01486 int y = v->y_pos + _delta_coord[v->direction + 8];
01487
01488 GetNewVehiclePosResult gp;
01489 gp.x = x;
01490 gp.y = y;
01491 gp.old_tile = v->tile;
01492 gp.new_tile = TileVirtXY(x, y);
01493 return gp;
01494 }
01495
01496 static const Direction _new_direction_table[] = {
01497 DIR_N, DIR_NW, DIR_W,
01498 DIR_NE, DIR_SE, DIR_SW,
01499 DIR_E, DIR_SE, DIR_S
01500 };
01501
01502 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01503 {
01504 int i = 0;
01505
01506 if (y >= v->y_pos) {
01507 if (y != v->y_pos) i += 3;
01508 i += 3;
01509 }
01510
01511 if (x >= v->x_pos) {
01512 if (x != v->x_pos) i++;
01513 i++;
01514 }
01515
01516 Direction dir = v->direction;
01517
01518 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01519 if (dirdiff == DIRDIFF_SAME) return dir;
01520 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01521 }
01522
01532 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01533 {
01534 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01535 }
01536
01544 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01545 {
01546
01547 const Vehicle *v;
01548 FOR_ALL_VEHICLES(v) {
01549 if (v->type == type && v->owner == owner) {
01550 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01551 }
01552 }
01553
01554 if (this->maxid == 0) return;
01555
01556
01557
01558
01559 this->cache = CallocT<bool>(this->maxid + 2);
01560
01561
01562 FOR_ALL_VEHICLES(v) {
01563 if (v->type == type && v->owner == owner) {
01564 this->cache[v->unitnumber] = true;
01565 }
01566 }
01567 }
01568
01570 UnitID FreeUnitIDGenerator::NextID()
01571 {
01572 if (this->maxid <= this->curid) return ++this->curid;
01573
01574 while (this->cache[++this->curid]) { }
01575
01576 return this->curid;
01577 }
01578
01584 UnitID GetFreeUnitNumber(VehicleType type)
01585 {
01586
01587 uint max_veh;
01588 switch (type) {
01589 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
01590 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
01591 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
01592 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
01593 default: NOT_REACHED();
01594 }
01595
01596 const Company *c = Company::Get(_current_company);
01597 if (c->group_all[type].num_vehicle >= max_veh) return UINT16_MAX;
01598
01599 FreeUnitIDGenerator gen(type, _current_company);
01600
01601 return gen.NextID();
01602 }
01603
01604
01613 bool CanBuildVehicleInfrastructure(VehicleType type)
01614 {
01615 assert(IsCompanyBuildableVehicleType(type));
01616
01617 if (!Company::IsValidID(_local_company)) return false;
01618 if (!_settings_client.gui.disable_unsuitable_building) return true;
01619
01620 UnitID max;
01621 switch (type) {
01622 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
01623 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
01624 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
01625 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01626 default: NOT_REACHED();
01627 }
01628
01629
01630 if (max > 0) {
01631
01632 const Engine *e;
01633 FOR_ALL_ENGINES_OF_TYPE(e, type) {
01634 if (HasBit(e->company_avail, _local_company)) return true;
01635 }
01636 return false;
01637 }
01638
01639
01640 const Vehicle *v;
01641 FOR_ALL_VEHICLES(v) {
01642 if (v->owner == _local_company && v->type == type) return true;
01643 }
01644
01645 return false;
01646 }
01647
01648
01656 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
01657 {
01658 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01659 const Engine *e = Engine::Get(engine_type);
01660 switch (e->type) {
01661 default: NOT_REACHED();
01662 case VEH_TRAIN:
01663 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
01664
01665
01666 engine_type = parent_engine_type;
01667 e = Engine::Get(engine_type);
01668
01669 }
01670
01671 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01672 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01673 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
01674 if (!CargoSpec::Get(cargo_type)->is_freight) {
01675 if (parent_engine_type == INVALID_ENGINE) {
01676 return LS_PASSENGER_WAGON_STEAM;
01677 } else {
01678 switch (RailVehInfo(parent_engine_type)->engclass) {
01679 default: NOT_REACHED();
01680 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
01681 case EC_DIESEL: return LS_PASSENGER_WAGON_DIESEL;
01682 case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
01683 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
01684 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
01685 }
01686 }
01687 } else {
01688 return LS_FREIGHT_WAGON;
01689 }
01690 } else {
01691 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
01692
01693 switch (e->u.rail.engclass) {
01694 default: NOT_REACHED();
01695 case EC_STEAM: return LS_STEAM;
01696 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
01697 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
01698 case EC_MONORAIL: return LS_MONORAIL;
01699 case EC_MAGLEV: return LS_MAGLEV;
01700 }
01701 }
01702
01703 case VEH_ROAD:
01704
01705 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01706 engine_type = parent_engine_type;
01707 e = Engine::Get(engine_type);
01708 cargo_type = v->First()->cargo_type;
01709 }
01710 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01711 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01712
01713
01714 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
01715
01716 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01717 } else {
01718
01719 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01720 }
01721
01722 case VEH_SHIP:
01723 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01724 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01725 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01726
01727 case VEH_AIRCRAFT:
01728 switch (e->u.air.subtype) {
01729 case AIR_HELI: return LS_HELICOPTER;
01730 case AIR_CTOL: return LS_SMALL_PLANE;
01731 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
01732 default: NOT_REACHED();
01733 }
01734 }
01735 }
01736
01746 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
01747 {
01748 const Company *c = Company::Get(company);
01749 LiveryScheme scheme = LS_DEFAULT;
01750
01751
01752
01753 if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
01754
01755 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
01756
01757
01758 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01759 }
01760
01761 return &c->livery[scheme];
01762 }
01763
01764
01765 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01766 {
01767 PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01768
01769
01770 if (map != PAL_NONE) return map;
01771
01772 const Engine *e = Engine::Get(engine_type);
01773
01774
01775 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
01776 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01777
01778 if (callback != CALLBACK_FAILED) {
01779 assert_compile(PAL_NONE == 0);
01780 map = GB(callback, 0, 14);
01781
01782
01783 if (!HasBit(callback, 14)) {
01784
01785 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01786 return map;
01787 }
01788 }
01789 }
01790
01791 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
01792
01793 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
01794
01795
01796 if (!Company::IsValidID(company)) return map;
01797
01798 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
01799
01800 map += livery->colour1;
01801 if (twocc) map += livery->colour2 * 16;
01802
01803
01804 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
01805 return map;
01806 }
01807
01814 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
01815 {
01816 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01817 }
01818
01824 PaletteID GetVehiclePalette(const Vehicle *v)
01825 {
01826 if (v->IsGroundVehicle()) {
01827 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
01828 }
01829
01830 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01831 }
01832
01836 void Vehicle::DeleteUnreachedImplicitOrders()
01837 {
01838 if (this->IsGroundVehicle()) {
01839 uint16 &gv_flags = this->GetGroundVehicleFlags();
01840 if (HasBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)) {
01841
01842 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
01843 this->cur_implicit_order_index = this->cur_real_order_index;
01844 InvalidateVehicleOrder(this, 0);
01845 return;
01846 }
01847 }
01848
01849 const Order *order = this->GetOrder(this->cur_implicit_order_index);
01850 while (order != NULL) {
01851 if (this->cur_implicit_order_index == this->cur_real_order_index) break;
01852
01853 if (order->IsType(OT_IMPLICIT)) {
01854 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->orders.list->GetNextStoppingOrder(this,
02089 this->GetOrder(this->cur_implicit_order_index), hops);
02090 const Order *cur = first;
02091 const Order *next = first;
02092 while (next != NULL && cur->CanLeaveWithCargo(true)) {
02093 next = this->orders.list->GetNextStoppingOrder(this,
02094 this->orders.list->GetNext(next), ++hops);
02095 if (next == NULL) break;
02096
02097 if (next->IsType(OT_GOTO_DEPOT)) {
02098
02099 CargoID new_cid = next->GetRefitCargo();
02100 byte new_subtype = next->GetRefitSubtype();
02101 for (Vehicle *v = this; v != NULL; v = v->Next()) {
02102 const Engine *e = Engine::Get(v->engine_type);
02103 if (!HasBit(e->info.refit_mask, new_cid)) continue;
02104
02105
02106 CargoID temp_cid = v->cargo_type;
02107 byte temp_subtype = v->cargo_subtype;
02108 v->cargo_type = new_cid;
02109 v->cargo_subtype = new_subtype;
02110
02111 uint16 mail_capacity = 0;
02112 uint amount = e->DetermineCapacity(v, &mail_capacity);
02113
02114
02115 v->cargo_type = temp_cid;
02116 v->cargo_subtype = temp_subtype;
02117
02118
02119 if (new_cid != v->cargo_type && v->refit_cap > 0) {
02120 capacities[v->cargo_type] -= v->refit_cap;
02121 v->refit_cap = 0;
02122 } else if (amount < v->refit_cap) {
02123 capacities[v->cargo_type] -= v->refit_cap - amount;
02124 v->refit_cap = amount;
02125 }
02126
02127
02128 if (v->type == VEH_AIRCRAFT) {
02129 Vehicle *u = v->Next();
02130 if (mail_capacity < u->refit_cap) {
02131 capacities[u->cargo_type] -= u->refit_cap - mail_capacity;
02132 u->refit_cap = mail_capacity;
02133 }
02134 break;
02135 }
02136 }
02137 } else {
02138 StationID next_station = next->GetDestination();
02139 Station *st = Station::GetIfValid(cur->GetDestination());
02140 if (st != NULL && next_station != INVALID_STATION && next_station != st->index) {
02141 for (const SmallPair<CargoID, uint> *i = capacities.Begin(); i != capacities.End(); ++i) {
02142
02143 if (i->second > 0) IncreaseStats(st, i->first, next_station, i->second, UINT_MAX);
02144 }
02145 }
02146 cur = next;
02147 if (cur == first) break;
02148 }
02149 }
02150
02151 for (Vehicle *v = this; v != NULL; v = v->Next()) v->refit_cap = v->cargo_cap;
02152 }
02153
02159 void Vehicle::HandleLoading(bool mode)
02160 {
02161 switch (this->current_order.GetType()) {
02162 case OT_LOADING: {
02163 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
02164
02165
02166 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
02167
02168 this->PlayLeaveStationSound();
02169
02170 this->LeaveStation();
02171
02172
02173 const Order *order = this->GetOrder(this->cur_implicit_order_index);
02174 if (order == NULL ||
02175 (!order->IsType(OT_IMPLICIT) && !order->IsType(OT_GOTO_STATION)) ||
02176 order->GetDestination() != this->last_station_visited) {
02177 return;
02178 }
02179 break;
02180 }
02181
02182 case OT_DUMMY: break;
02183
02184 default: return;
02185 }
02186
02187 this->IncrementImplicitOrderIndex();
02188 }
02189
02194 void Vehicle::GetConsistFreeCapacities(SmallMap<CargoID, uint> &capacities) const
02195 {
02196 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
02197 if (v->cargo_cap == 0) continue;
02198 SmallPair<CargoID, uint> *pair = capacities.Find(v->cargo_type);
02199 if (pair == capacities.End()) {
02200 pair = capacities.Append();
02201 pair->first = v->cargo_type;
02202 pair->second = v->cargo_cap - v->cargo.Count();
02203 } else {
02204 pair->second += v->cargo_cap - v->cargo.Count();
02205 }
02206 }
02207 }
02208
02209 uint Vehicle::GetConsistTotalCapacity() const
02210 {
02211 uint result = 0;
02212 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
02213 result += v->cargo_cap;
02214 }
02215 return result;
02216 }
02217
02224 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
02225 {
02226 CommandCost ret = CheckOwnership(this->owner);
02227 if (ret.Failed()) return ret;
02228
02229 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
02230 if (this->IsStoppedInDepot()) return CMD_ERROR;
02231
02232 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
02233 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
02234 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
02235
02236
02237
02238 if (flags & DC_EXEC) {
02239 this->current_order.SetDepotOrderType(ODTF_MANUAL);
02240 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
02241 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02242 }
02243 return CommandCost();
02244 }
02245
02246 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR;
02247 if (flags & DC_EXEC) {
02248
02249
02250 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
02251
02252 if (this->IsGroundVehicle()) {
02253 uint16 &gv_flags = this->GetGroundVehicleFlags();
02254 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02255 }
02256
02257 this->current_order.MakeDummy();
02258 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02259 }
02260 return CommandCost();
02261 }
02262
02263 TileIndex location;
02264 DestinationID destination;
02265 bool reverse;
02266 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};
02267 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
02268
02269 if (flags & DC_EXEC) {
02270 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
02271
02272 if (this->IsGroundVehicle()) {
02273 uint16 &gv_flags = this->GetGroundVehicleFlags();
02274 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
02275 }
02276
02277 this->dest_tile = location;
02278 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
02279 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
02280 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
02281
02282
02283 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
02284
02285 if (this->type == VEH_AIRCRAFT) {
02286 Aircraft *a = Aircraft::From(this);
02287 if (a->state == FLYING && a->targetairport != destination) {
02288
02289 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
02290 AircraftNextAirportPos_and_Order(a);
02291 }
02292 }
02293 }
02294
02295 return CommandCost();
02296
02297 }
02298
02303 void Vehicle::UpdateVisualEffect(bool allow_power_change)
02304 {
02305 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02306 const Engine *e = this->GetEngine();
02307
02308
02309 byte visual_effect;
02310 switch (e->type) {
02311 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
02312 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
02313 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
02314 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
02315 }
02316
02317
02318 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
02319 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
02320
02321 if (callback != CALLBACK_FAILED) {
02322 if (callback >= 0x100 && e->GetGRF()->grf_version >= 8) ErrorUnknownCallbackResult(e->GetGRFID(), CBID_VEHICLE_VISUAL_EFFECT, callback);
02323
02324 callback = GB(callback, 0, 8);
02325
02326
02327 if (callback == VE_DEFAULT) {
02328 assert(HasBit(callback, VE_DISABLE_EFFECT));
02329 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
02330 }
02331 visual_effect = callback;
02332 }
02333 }
02334
02335
02336 if (visual_effect == VE_DEFAULT ||
02337 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
02338
02339
02340 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
02341 if (visual_effect == VE_DEFAULT) {
02342 visual_effect = 1 << VE_DISABLE_EFFECT;
02343 } else {
02344 SetBit(visual_effect, VE_DISABLE_EFFECT);
02345 }
02346 } else {
02347 if (visual_effect == VE_DEFAULT) {
02348
02349 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
02350 }
02351 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
02352 }
02353 }
02354
02355 this->vcache.cached_vis_effect = visual_effect;
02356
02357 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
02358 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
02359 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
02360 }
02361 }
02362
02363 static const int8 _vehicle_smoke_pos[8] = {
02364 1, 1, 1, 0, -1, -1, -1, 0
02365 };
02366
02371 void Vehicle::ShowVisualEffect() const
02372 {
02373 assert(this->IsPrimaryVehicle());
02374 bool sound = false;
02375
02376
02377
02378
02379
02380
02381 if (_settings_game.vehicle.smoke_amount == 0 ||
02382 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
02383 this->cur_speed < 2) {
02384 return;
02385 }
02386
02387 uint max_speed = this->vcache.cached_max_speed;
02388 if (this->type == VEH_TRAIN) {
02389 const Train *t = Train::From(this);
02390
02391
02392
02393
02394 if (HasBit(t->flags, VRF_REVERSING) ||
02395 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
02396 t->cur_speed >= t->Train::GetCurrentMaxSpeed())) {
02397 return;
02398 }
02399
02400 max_speed = min(max_speed, t->gcache.cached_max_track_speed);
02401 max_speed = min(max_speed, this->current_order.max_speed);
02402 }
02403 if (this->type == VEH_ROAD || this->type == VEH_SHIP) max_speed = min(max_speed, this->current_order.max_speed * 2);
02404
02405 const Vehicle *v = this;
02406
02407 do {
02408 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
02409 byte effect_type = GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
02410 bool disable_effect = HasBit(v->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
02411
02412
02413
02414
02415
02416
02417
02418
02419 if (disable_effect ||
02420 v->vehstatus & VS_HIDDEN ||
02421 (MayHaveBridgeAbove(v->tile) && IsBridgeAbove(v->tile)) ||
02422 IsDepotTile(v->tile) ||
02423 IsTunnelTile(v->tile) ||
02424 (v->type == VEH_TRAIN &&
02425 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
02426 continue;
02427 }
02428
02429
02430
02431
02432 if (v->type == VEH_TRAIN) effect_offset += (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
02433
02434 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
02435 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
02436
02437 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
02438 x = -x;
02439 y = -y;
02440 }
02441
02442 switch (effect_type) {
02443 case VE_TYPE_STEAM:
02444
02445
02446
02447
02448
02449 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / max_speed))) == 0) {
02450 CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
02451 sound = true;
02452 }
02453 break;
02454
02455 case VE_TYPE_DIESEL: {
02456
02457
02458
02459
02460
02461
02462
02463
02464
02465
02466
02467 int power_weight_effect = 0;
02468 if (v->type == VEH_TRAIN) {
02469 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
02470 }
02471 if (this->cur_speed < (max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
02472 Chance16((64 - ((this->cur_speed << 5) / max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
02473 CreateEffectVehicleRel(v, x, y, 10, EV_DIESEL_SMOKE);
02474 sound = true;
02475 }
02476 break;
02477 }
02478
02479 case VE_TYPE_ELECTRIC:
02480
02481
02482
02483
02484
02485
02486 if (GB(v->tick_counter, 0, 2) == 0 &&
02487 Chance16((6 - ((this->cur_speed << 2) / max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
02488 CreateEffectVehicleRel(v, x, y, 10, EV_ELECTRIC_SPARK);
02489 sound = true;
02490 }
02491 break;
02492
02493 default:
02494 break;
02495 }
02496 } while ((v = v->Next()) != NULL);
02497
02498 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
02499 }
02500
02505 void Vehicle::SetNext(Vehicle *next)
02506 {
02507 assert(this != next);
02508
02509 if (this->next != NULL) {
02510
02511 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02512 v->first = this->next;
02513 }
02514 this->next->previous = NULL;
02515 }
02516
02517 this->next = next;
02518
02519 if (this->next != NULL) {
02520
02521 if (this->next->previous != NULL) this->next->previous->next = NULL;
02522 this->next->previous = this;
02523 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02524 v->first = this->first;
02525 }
02526 }
02527 }
02528
02534 void Vehicle::AddToShared(Vehicle *shared_chain)
02535 {
02536 assert(this->previous_shared == NULL && this->next_shared == NULL);
02537
02538 if (shared_chain->orders.list == NULL) {
02539 assert(shared_chain->previous_shared == NULL);
02540 assert(shared_chain->next_shared == NULL);
02541 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
02542 }
02543
02544 this->next_shared = shared_chain->next_shared;
02545 this->previous_shared = shared_chain;
02546
02547 shared_chain->next_shared = this;
02548
02549 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
02550
02551 shared_chain->orders.list->AddVehicle(this);
02552 }
02553
02557 void Vehicle::RemoveFromShared()
02558 {
02559
02560
02561 bool were_first = (this->FirstShared() == this);
02562 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
02563
02564 this->orders.list->RemoveVehicle(this);
02565
02566 if (!were_first) {
02567
02568 this->previous_shared->next_shared = this->NextShared();
02569 }
02570
02571 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
02572
02573
02574 if (this->orders.list->GetNumVehicles() == 1) {
02575
02576 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
02577 InvalidateVehicleOrder(this->FirstShared(), 0);
02578 } else if (were_first) {
02579
02580
02581 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
02582 }
02583
02584 this->next_shared = NULL;
02585 this->previous_shared = NULL;
02586 }
02587
02588 void VehiclesYearlyLoop()
02589 {
02590 Vehicle *v;
02591 FOR_ALL_VEHICLES(v) {
02592 if (v->IsPrimaryVehicle()) {
02593
02594 Money profit = v->GetDisplayProfitThisYear();
02595 if (v->age >= 730 && profit < 0) {
02596 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
02597 SetDParam(0, v->index);
02598 SetDParam(1, profit);
02599 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_UNPROFITABLE, v->index);
02600 }
02601 AI::NewEvent(v->owner, new ScriptEventVehicleUnprofitable(v->index));
02602 }
02603
02604 v->profit_last_year = v->profit_this_year;
02605 v->profit_this_year = 0;
02606 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
02607 }
02608 }
02609 GroupStatistics::UpdateProfits();
02610 SetWindowClassesDirty(WC_TRAINS_LIST);
02611 SetWindowClassesDirty(WC_SHIPS_LIST);
02612 SetWindowClassesDirty(WC_ROADVEH_LIST);
02613 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
02614 }
02615
02616
02626 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
02627 {
02628 const Engine *e = Engine::GetIfValid(engine_type);
02629 assert(e != NULL);
02630
02631 switch (e->type) {
02632 case VEH_TRAIN:
02633 return (st->facilities & FACIL_TRAIN) != 0;
02634
02635 case VEH_ROAD:
02636
02637
02638
02639 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
02640
02641 case VEH_SHIP:
02642 return (st->facilities & FACIL_DOCK) != 0;
02643
02644 case VEH_AIRCRAFT:
02645 return (st->facilities & FACIL_AIRPORT) != 0 &&
02646 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
02647
02648 default:
02649 return false;
02650 }
02651 }
02652
02659 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
02660 {
02661 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
02662
02663 return CanVehicleUseStation(v->engine_type, st);
02664 }
02665
02671 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
02672 {
02673 assert(this->IsGroundVehicle());
02674 if (this->type == VEH_TRAIN) {
02675 return &Train::From(this)->gcache;
02676 } else {
02677 return &RoadVehicle::From(this)->gcache;
02678 }
02679 }
02680
02686 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
02687 {
02688 assert(this->IsGroundVehicle());
02689 if (this->type == VEH_TRAIN) {
02690 return &Train::From(this)->gcache;
02691 } else {
02692 return &RoadVehicle::From(this)->gcache;
02693 }
02694 }
02695
02701 uint16 &Vehicle::GetGroundVehicleFlags()
02702 {
02703 assert(this->IsGroundVehicle());
02704 if (this->type == VEH_TRAIN) {
02705 return Train::From(this)->gv_flags;
02706 } else {
02707 return RoadVehicle::From(this)->gv_flags;
02708 }
02709 }
02710
02716 const uint16 &Vehicle::GetGroundVehicleFlags() const
02717 {
02718 assert(this->IsGroundVehicle());
02719 if (this->type == VEH_TRAIN) {
02720 return Train::From(this)->gv_flags;
02721 } else {
02722 return RoadVehicle::From(this)->gv_flags;
02723 }
02724 }
02725
02734 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
02735 {
02736 if (v->type == VEH_TRAIN) {
02737 Train *u = Train::From(v);
02738
02739 u = u->GetFirstEnginePart();
02740
02741
02742 for (; u != NULL && num_vehicles > 0; num_vehicles--) {
02743 do {
02744
02745 set.Include(u->index);
02746
02747
02748 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
02749
02750 u = u->Next();
02751 } while (u != NULL && u->IsArticulatedPart());
02752 }
02753 }
02754 }