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