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