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