00001
00002
00005 #include "stdafx.h"
00006 #include "gui.h"
00007 #include "openttd.h"
00008 #include "debug.h"
00009 #include "roadveh.h"
00010 #include "ship.h"
00011 #include "spritecache.h"
00012 #include "landscape.h"
00013 #include "timetable.h"
00014 #include "viewport_func.h"
00015 #include "news_func.h"
00016 #include "command_func.h"
00017 #include "company_func.h"
00018 #include "vehicle_gui.h"
00019 #include "train.h"
00020 #include "aircraft.h"
00021 #include "newgrf_engine.h"
00022 #include "newgrf_sound.h"
00023 #include "newgrf_station.h"
00024 #include "group.h"
00025 #include "group_gui.h"
00026 #include "strings_func.h"
00027 #include "zoom_func.h"
00028 #include "functions.h"
00029 #include "date_func.h"
00030 #include "window_func.h"
00031 #include "vehicle_func.h"
00032 #include "autoreplace_func.h"
00033 #include "autoreplace_gui.h"
00034 #include "oldpool_func.h"
00035 #include "ai/ai.hpp"
00036 #include "core/smallmap_type.hpp"
00037 #include "depot_func.h"
00038 #include "settings_type.h"
00039 #include "network/network.h"
00040
00041 #include "economy_base.h"
00042 #include "table/sprites.h"
00043 #include "table/strings.h"
00044
00045 #define GEN_HASH(x, y) ((GB((y), 6, 6) << 6) + GB((x), 7, 6))
00046
00047 VehicleID _vehicle_id_ctr_day;
00048 const Vehicle *_place_clicked_vehicle;
00049 VehicleID _new_vehicle_id;
00050 uint16 _returned_refit_capacity;
00051
00052
00053
00054 DEFINE_OLD_POOL_GENERIC(Vehicle, Vehicle)
00055
00056
00060 bool Vehicle::NeedsAutorenewing(const Company *c) const
00061 {
00062
00063
00064
00065
00066 assert(c == GetCompany(this->owner));
00067
00068 if (!c->engine_renew) return false;
00069 if (this->age - this->max_age < (c->engine_renew_months * 30)) return false;
00070 if (this->age == 0) return false;
00071
00072 return true;
00073 }
00074
00075 void VehicleServiceInDepot(Vehicle *v)
00076 {
00077 v->date_of_last_service = _date;
00078 v->breakdowns_since_last_service = 0;
00079 v->reliability = GetEngine(v->engine_type)->reliability;
00080 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
00081 }
00082
00083 bool Vehicle::NeedsServicing() const
00084 {
00085 if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
00086
00087 if (_settings_game.order.no_servicing_if_no_breakdowns && _settings_game.difficulty.vehicle_breakdowns == 0) {
00088
00089
00090 return EngineHasReplacementForCompany(GetCompany(this->owner), this->engine_type, this->group_id);
00091 }
00092
00093 return _settings_game.vehicle.servint_ispercent ?
00094 (this->reliability < GetEngine(this->engine_type)->reliability * (100 - this->service_interval) / 100) :
00095 (this->date_of_last_service + this->service_interval < _date);
00096 }
00097
00098 bool Vehicle::NeedsAutomaticServicing() const
00099 {
00100 if (_settings_game.order.gotodepot && VehicleHasDepotOrders(this)) return false;
00101 if (this->current_order.IsType(OT_LOADING)) return false;
00102 if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
00103 return NeedsServicing();
00104 }
00105
00114 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
00115 {
00116 const Engine *e = GetEngine(engine);
00117 uint32 grfid = e->grffile->grfid;
00118 GRFConfig *grfconfig = GetGRFConfig(grfid);
00119
00120 if (!HasBit(grfconfig->grf_bugs, bug_type)) {
00121 SetBit(grfconfig->grf_bugs, bug_type);
00122 SetDParamStr(0, grfconfig->name);
00123 SetDParam(1, engine);
00124 ShowErrorMessage(part2, part1, 0, 0);
00125 if (!_networking) _pause_game = (critical ? -1 : 1);
00126 }
00127
00128
00129 char buffer[512];
00130
00131 SetDParamStr(0, grfconfig->name);
00132 GetString(buffer, part1, lastof(buffer));
00133 DEBUG(grf, 0, "%s", buffer + 3);
00134
00135 SetDParam(1, engine);
00136 GetString(buffer, part2, lastof(buffer));
00137 DEBUG(grf, 0, "%s", buffer + 3);
00138 }
00139
00140 StringID VehicleInTheWayErrMsg(const Vehicle *v)
00141 {
00142 switch (v->type) {
00143 case VEH_TRAIN: return STR_8803_TRAIN_IN_THE_WAY;
00144 case VEH_ROAD: return STR_9000_ROAD_VEHICLE_IN_THE_WAY;
00145 case VEH_AIRCRAFT: return STR_A015_AIRCRAFT_IN_THE_WAY;
00146 default: return STR_980E_SHIP_IN_THE_WAY;
00147 }
00148 }
00149
00150 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
00151 {
00152 byte z = *(byte*)data;
00153
00154 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
00155 if (v->z_pos > z) return NULL;
00156
00157 _error_message = VehicleInTheWayErrMsg(v);
00158 return v;
00159 }
00160
00161 bool EnsureNoVehicleOnGround(TileIndex tile)
00162 {
00163 byte z = GetTileMaxZ(tile);
00164 return !HasVehicleOnPos(tile, &z, &EnsureNoVehicleProcZ);
00165 }
00166
00168 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
00169 {
00170 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
00171 if (v == (const Vehicle *)data) return NULL;
00172
00173 _error_message = VehicleInTheWayErrMsg(v);
00174 return v;
00175 }
00176
00184 bool HasVehicleOnTunnelBridge(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
00185 {
00186 return HasVehicleOnPos(tile, (void *)ignore, &GetVehicleTunnelBridgeProc) ||
00187 HasVehicleOnPos(endtile, (void *)ignore, &GetVehicleTunnelBridgeProc);
00188 }
00189
00190
00191 Vehicle::Vehicle()
00192 {
00193 this->type = VEH_INVALID;
00194 this->coord.left = INVALID_COORD;
00195 this->group_id = DEFAULT_GROUP;
00196 this->fill_percent_te_id = INVALID_TE_ID;
00197 this->first = this;
00198 this->colourmap = PAL_NONE;
00199 }
00200
00205 byte VehicleRandomBits()
00206 {
00207 return GB(Random(), 0, 8);
00208 }
00209
00210
00211 bool Vehicle::AllocateList(Vehicle **vl, int num)
00212 {
00213 if (!Vehicle::CanAllocateItem(num)) return false;
00214 if (vl == NULL) return true;
00215
00216 uint counter = _Vehicle_pool.first_free_index;
00217
00218 for (int i = 0; i != num; i++) {
00219 vl[i] = new (AllocateRaw(counter)) InvalidVehicle();
00220 counter++;
00221 }
00222
00223 return true;
00224 }
00225
00226
00227
00228 const int HASH_BITS = 7;
00229 const int HASH_SIZE = 1 << HASH_BITS;
00230 const int HASH_MASK = HASH_SIZE - 1;
00231 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
00232 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
00233
00234
00235
00236 const int HASH_RES = 0;
00237
00238 static Vehicle *_new_vehicle_position_hash[TOTAL_HASH_SIZE];
00239
00240 static Vehicle *VehicleFromHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
00241 {
00242 for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
00243 for (int x = xl; ; x = (x + 1) & HASH_MASK) {
00244 Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00245 for (; v != NULL; v = v->next_new_hash) {
00246 Vehicle *a = proc(v, data);
00247 if (find_first && a != NULL) return a;
00248 }
00249 if (x == xu) break;
00250 }
00251 if (y == yu) break;
00252 }
00253
00254 return NULL;
00255 }
00256
00257
00269 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
00270 {
00271 const int COLL_DIST = 6;
00272
00273
00274 int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00275 int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00276 int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00277 int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00278
00279 return VehicleFromHash(xl, yl, xu, yu, data, proc, find_first);
00280 }
00281
00296 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00297 {
00298 VehicleFromPosXY(x, y, data, proc, false);
00299 }
00300
00312 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00313 {
00314 return VehicleFromPosXY(x, y, data, proc, true) != NULL;
00315 }
00316
00327 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
00328 {
00329 int x = GB(TileX(tile), HASH_RES, HASH_BITS);
00330 int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
00331
00332 Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00333 for (; v != NULL; v = v->next_new_hash) {
00334 if (v->tile != tile) continue;
00335
00336 Vehicle *a = proc(v, data);
00337 if (find_first && a != NULL) return a;
00338 }
00339
00340 return NULL;
00341 }
00342
00356 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00357 {
00358 VehicleFromPos(tile, data, proc, false);
00359 }
00360
00371 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00372 {
00373 return VehicleFromPos(tile, data, proc, true) != NULL;
00374 }
00375
00376
00377 static void UpdateNewVehiclePosHash(Vehicle *v, bool remove)
00378 {
00379 Vehicle **old_hash = v->old_new_hash;
00380 Vehicle **new_hash;
00381
00382 if (remove) {
00383 new_hash = NULL;
00384 } else {
00385 int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
00386 int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
00387 new_hash = &_new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00388 }
00389
00390 if (old_hash == new_hash) return;
00391
00392
00393 if (old_hash != NULL) {
00394 Vehicle *last = NULL;
00395 Vehicle *u = *old_hash;
00396 while (u != v) {
00397 last = u;
00398 u = u->next_new_hash;
00399 assert(u != NULL);
00400 }
00401
00402 if (last == NULL) {
00403 *old_hash = v->next_new_hash;
00404 } else {
00405 last->next_new_hash = v->next_new_hash;
00406 }
00407 }
00408
00409
00410 if (new_hash != NULL) {
00411 v->next_new_hash = *new_hash;
00412 *new_hash = v;
00413 assert(v != v->next_new_hash);
00414 }
00415
00416
00417 v->old_new_hash = new_hash;
00418 }
00419
00420 static Vehicle *_vehicle_position_hash[0x1000];
00421
00422 static void UpdateVehiclePosHash(Vehicle *v, int x, int y)
00423 {
00424 UpdateNewVehiclePosHash(v, x == INVALID_COORD);
00425
00426 Vehicle **old_hash, **new_hash;
00427 int old_x = v->coord.left;
00428 int old_y = v->coord.top;
00429
00430 new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(x, y)];
00431 old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(old_x, old_y)];
00432
00433 if (old_hash == new_hash) return;
00434
00435
00436 if (old_hash != NULL) {
00437 Vehicle *last = NULL;
00438 Vehicle *u = *old_hash;
00439 while (u != v) {
00440 last = u;
00441 u = u->next_hash;
00442 assert(u != NULL);
00443 }
00444
00445 if (last == NULL) {
00446 *old_hash = v->next_hash;
00447 } else {
00448 last->next_hash = v->next_hash;
00449 }
00450 }
00451
00452
00453 if (new_hash != NULL) {
00454 v->next_hash = *new_hash;
00455 *new_hash = v;
00456 }
00457 }
00458
00459 void ResetVehiclePosHash()
00460 {
00461 Vehicle *v;
00462 FOR_ALL_VEHICLES(v) { v->old_new_hash = NULL; }
00463 memset(_vehicle_position_hash, 0, sizeof(_vehicle_position_hash));
00464 memset(_new_vehicle_position_hash, 0, sizeof(_new_vehicle_position_hash));
00465 }
00466
00467 void ResetVehicleColourMap()
00468 {
00469 Vehicle *v;
00470 FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
00471 }
00472
00477 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
00478 static AutoreplaceMap _vehicles_to_autoreplace;
00479
00480 void InitializeVehicles()
00481 {
00482 _Vehicle_pool.CleanPool();
00483 _Vehicle_pool.AddBlockToPool();
00484
00485 _CargoPayment_pool.CleanPool();
00486 _CargoPayment_pool.AddBlockToPool();
00487
00488 _cargo_payment_savegame = false;
00489
00490 _vehicles_to_autoreplace.Reset();
00491 ResetVehiclePosHash();
00492 }
00493
00494 Vehicle *GetLastVehicleInChain(Vehicle *v)
00495 {
00496 while (v->Next() != NULL) v = v->Next();
00497 return v;
00498 }
00499
00500 const Vehicle *GetLastVehicleInChain(const Vehicle *v)
00501 {
00502 while (v->Next() != NULL) v = v->Next();
00503 return v;
00504 }
00505
00506 uint CountVehiclesInChain(const Vehicle *v)
00507 {
00508 uint count = 0;
00509 do count++; while ((v = v->Next()) != NULL);
00510 return count;
00511 }
00512
00517 bool IsEngineCountable(const Vehicle *v)
00518 {
00519 switch (v->type) {
00520 case VEH_AIRCRAFT: return IsNormalAircraft(v);
00521 case VEH_TRAIN:
00522 return !IsArticulatedPart(v) &&
00523 !IsRearDualheaded(v);
00524 case VEH_ROAD: return IsRoadVehFront(v);
00525 case VEH_SHIP: return true;
00526 default: return false;
00527 }
00528 }
00529
00530 void Vehicle::PreDestructor()
00531 {
00532 if (CleaningPool()) return;
00533
00534 if (IsValidStationID(this->last_station_visited)) {
00535 GetStation(this->last_station_visited)->loading_vehicles.remove(this);
00536
00537 HideFillingPercent(&this->fill_percent_te_id);
00538 }
00539
00540 if (IsEngineCountable(this)) {
00541 GetCompany(this->owner)->num_engines[this->engine_type]--;
00542 if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
00543
00544 DeleteGroupHighlightOfVehicle(this);
00545 if (IsValidGroupID(this->group_id)) GetGroup(this->group_id)->num_engines[this->engine_type]--;
00546 if (this->IsPrimaryVehicle()) DecreaseGroupNumVehicle(this->group_id);
00547 }
00548
00549 if (this->type == VEH_ROAD) ClearSlot(this);
00550 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
00551 Station *st = GetTargetAirportIfValid(this);
00552 if (st != NULL) {
00553 const AirportFTA *layout = st->Airport()->layout;
00554 CLRBITS(st->airport_flags, layout[this->u.air.previous_pos].block | layout[this->u.air.pos].block);
00555 }
00556 }
00557
00558 if (this->type != VEH_TRAIN || (this->type == VEH_TRAIN && (IsFrontEngine(this) || IsFreeWagon(this)))) {
00559 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
00560 }
00561
00562 if (this->IsPrimaryVehicle()) {
00563 DeleteWindowById(WC_VEHICLE_VIEW, this->index);
00564 DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
00565 DeleteWindowById(WC_VEHICLE_REFIT, this->index);
00566 DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
00567 DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
00568 InvalidateWindow(WC_COMPANY, this->owner);
00569 }
00570 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00571
00572 this->cargo.Truncate(0);
00573 DeleteVehicleOrders(this);
00574 DeleteDepotHighlightOfVehicle(this);
00575
00576 extern void StopGlobalFollowVehicle(const Vehicle *v);
00577 StopGlobalFollowVehicle(this);
00578
00579 ReleaseDisastersTargetingVehicle(this->index);
00580 }
00581
00582 Vehicle::~Vehicle()
00583 {
00584 free(this->name);
00585
00586 if (CleaningPool()) return;
00587
00588
00589
00590 if (!(this->vehstatus & VS_HIDDEN)) MarkSingleVehicleDirty(this);
00591
00592 Vehicle *v = this->Next();
00593 this->SetNext(NULL);
00594
00595 delete v;
00596
00597 UpdateVehiclePosHash(this, INVALID_COORD, 0);
00598 this->next_hash = NULL;
00599 this->next_new_hash = NULL;
00600
00601 DeleteVehicleNews(this->index, INVALID_STRING_ID);
00602
00603 this->type = VEH_INVALID;
00604 }
00605
00609 void VehicleEnteredDepotThisTick(Vehicle *v)
00610 {
00611
00612 _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
00613
00614
00615
00616
00617
00618
00619 v->vehstatus |= VS_STOPPED;
00620 }
00621
00622 void CallVehicleTicks()
00623 {
00624 _vehicles_to_autoreplace.Clear();
00625
00626 Station *st;
00627 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
00628
00629 Vehicle *v;
00630 FOR_ALL_VEHICLES(v) {
00631 v->Tick();
00632
00633 switch (v->type) {
00634 default: break;
00635
00636 case VEH_TRAIN:
00637 case VEH_ROAD:
00638 case VEH_AIRCRAFT:
00639 case VEH_SHIP:
00640 if (v->type == VEH_TRAIN && IsTrainWagon(v)) continue;
00641 if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
00642 if (v->type == VEH_ROAD && !IsRoadVehFront(v)) continue;
00643
00644 v->motion_counter += (v->direction & 1) ? (v->cur_speed * 3) / 4 : v->cur_speed;
00645
00646 if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
00647
00648
00649 if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
00650 }
00651 }
00652
00653 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
00654 v = it->first;
00655
00656 _current_company = v->owner;
00657
00658
00659
00660
00661 if (it->second) v->vehstatus &= ~VS_STOPPED;
00662
00663
00664 int x = v->x_pos;
00665 int y = v->y_pos;
00666 int z = v->z_pos;
00667
00668 const Company *c = GetCompany(_current_company);
00669 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->engine_renew_money));
00670 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
00671 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->engine_renew_money));
00672
00673 if (!IsLocalCompany()) continue;
00674
00675 if (res.Succeeded()) {
00676 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
00677 continue;
00678 }
00679
00680 StringID error_message = res.GetErrorMessage();
00681 if (error_message == STR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
00682
00683 if (error_message == STR_0003_NOT_ENOUGH_CASH_REQUIRES) error_message = STR_AUTOREPLACE_MONEY_LIMIT;
00684
00685 StringID message;
00686 if (error_message == STR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
00687 message = error_message;
00688 } else {
00689 message = STR_VEHICLE_AUTORENEW_FAILED;
00690 }
00691
00692 SetDParam(0, v->index);
00693 SetDParam(1, error_message);
00694 AddNewsItem(message, NS_ADVICE, v->index, 0);
00695 }
00696
00697 _current_company = OWNER_NONE;
00698 }
00699
00705 bool CanRefitTo(EngineID engine_type, CargoID cid_to)
00706 {
00707 return HasBit(EngInfo(engine_type)->refit_mask, cid_to);
00708 }
00709
00714 CargoID FindFirstRefittableCargo(EngineID engine_type)
00715 {
00716 uint32 refit_mask = EngInfo(engine_type)->refit_mask;
00717
00718 if (refit_mask != 0) {
00719 for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
00720 if (HasBit(refit_mask, cid)) return cid;
00721 }
00722 }
00723
00724 return CT_INVALID;
00725 }
00726
00731 CommandCost GetRefitCost(EngineID engine_type)
00732 {
00733 Money base_cost;
00734 ExpensesType expense_type;
00735 switch (GetEngine(engine_type)->type) {
00736 case VEH_SHIP:
00737 base_cost = _price.ship_base;
00738 expense_type = EXPENSES_SHIP_RUN;
00739 break;
00740
00741 case VEH_ROAD:
00742 base_cost = _price.roadveh_base;
00743 expense_type = EXPENSES_ROADVEH_RUN;
00744 break;
00745
00746 case VEH_AIRCRAFT:
00747 base_cost = _price.aircraft_base;
00748 expense_type = EXPENSES_AIRCRAFT_RUN;
00749 break;
00750
00751 case VEH_TRAIN:
00752 base_cost = 2 * ((RailVehInfo(engine_type)->railveh_type == RAILVEH_WAGON) ?
00753 _price.build_railwagon : _price.build_railvehicle);
00754 expense_type = EXPENSES_TRAIN_RUN;
00755 break;
00756
00757 default: NOT_REACHED();
00758 }
00759 return CommandCost(expense_type, (EngInfo(engine_type)->refit_cost * base_cost) >> 10);
00760 }
00761
00762 static void DoDrawVehicle(const Vehicle *v)
00763 {
00764 SpriteID image = v->cur_image;
00765 SpriteID pal = PAL_NONE;
00766
00767 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
00768
00769 AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
00770 v->x_extent, v->y_extent, v->z_extent, v->z_pos, (v->vehstatus & VS_SHADOW) != 0);
00771 }
00772
00773 void ViewportAddVehicles(DrawPixelInfo *dpi)
00774 {
00775
00776 const int l = dpi->left;
00777 const int r = dpi->left + dpi->width;
00778 const int t = dpi->top;
00779 const int b = dpi->top + dpi->height;
00780
00781
00782 int xl, xu, yl, yu;
00783
00784 if (dpi->width + 70 < (1 << (7 + 6))) {
00785 xl = GB(l - 70, 7, 6);
00786 xu = GB(r, 7, 6);
00787 } else {
00788
00789 xl = 0;
00790 xu = 0x3F;
00791 }
00792
00793 if (dpi->height + 70 < (1 << (6 + 6))) {
00794 yl = GB(t - 70, 6, 6) << 6;
00795 yu = GB(b, 6, 6) << 6;
00796 } else {
00797
00798 yl = 0;
00799 yu = 0x3F << 6;
00800 }
00801
00802 for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
00803 for (int x = xl;; x = (x + 1) & 0x3F) {
00804 const Vehicle *v = _vehicle_position_hash[x + y];
00805
00806 while (v != NULL) {
00807 if (!(v->vehstatus & VS_HIDDEN) &&
00808 l <= v->coord.right &&
00809 t <= v->coord.bottom &&
00810 r >= v->coord.left &&
00811 b >= v->coord.top) {
00812 DoDrawVehicle(v);
00813 }
00814 v = v->next_hash;
00815 }
00816
00817 if (x == xu) break;
00818 }
00819
00820 if (y == yu) break;
00821 }
00822 }
00823
00824 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
00825 {
00826 Vehicle *found = NULL, *v;
00827 uint dist, best_dist = UINT_MAX;
00828
00829 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
00830
00831 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
00832 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
00833
00834 FOR_ALL_VEHICLES(v) {
00835 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
00836 x >= v->coord.left && x <= v->coord.right &&
00837 y >= v->coord.top && y <= v->coord.bottom) {
00838
00839 dist = max(
00840 abs(((v->coord.left + v->coord.right) >> 1) - x),
00841 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
00842 );
00843
00844 if (dist < best_dist) {
00845 found = v;
00846 best_dist = dist;
00847 }
00848 }
00849 }
00850
00851 return found;
00852 }
00853
00854 void CheckVehicle32Day(Vehicle *v)
00855 {
00856 if ((v->day_counter & 0x1F) != 0) return;
00857
00858 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
00859 if (callback == CALLBACK_FAILED) return;
00860 if (HasBit(callback, 0)) TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32);
00861 if (HasBit(callback, 1)) v->colourmap = PAL_NONE;
00862 }
00863
00864 void DecreaseVehicleValue(Vehicle *v)
00865 {
00866 v->value -= v->value >> 8;
00867 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
00868 }
00869
00870 static const byte _breakdown_chance[64] = {
00871 3, 3, 3, 3, 3, 3, 3, 3,
00872 4, 4, 5, 5, 6, 6, 7, 7,
00873 8, 8, 9, 9, 10, 10, 11, 11,
00874 12, 13, 13, 13, 13, 14, 15, 16,
00875 17, 19, 21, 25, 28, 31, 34, 37,
00876 40, 44, 48, 52, 56, 60, 64, 68,
00877 72, 80, 90, 100, 110, 120, 130, 140,
00878 150, 170, 190, 210, 230, 250, 250, 250,
00879 };
00880
00881 void CheckVehicleBreakdown(Vehicle *v)
00882 {
00883 int rel, rel_old;
00884
00885
00886 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
00887 if ((rel_old >> 8) != (rel >> 8)) InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
00888
00889 if (v->breakdown_ctr != 0 || v->vehstatus & VS_STOPPED ||
00890 _settings_game.difficulty.vehicle_breakdowns < 1 ||
00891 v->cur_speed < 5 || _game_mode == GM_MENU) {
00892 return;
00893 }
00894
00895 uint32 r = Random();
00896
00897
00898 int chance = v->breakdown_chance + 1;
00899 if (Chance16I(1, 25, r)) chance += 25;
00900 v->breakdown_chance = min(255, chance);
00901
00902
00903 rel = v->reliability;
00904 if (v->type == VEH_SHIP) rel += 0x6666;
00905
00906
00907 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
00908
00909
00910 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
00911 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
00912 v->breakdown_delay = GB(r, 24, 7) + 0x80;
00913 v->breakdown_chance = 0;
00914 }
00915 }
00916
00917 void AgeVehicle(Vehicle *v)
00918 {
00919 if (v->age < 65535) v->age++;
00920
00921 int age = v->age - v->max_age;
00922 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
00923 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
00924 v->reliability_spd_dec <<= 1;
00925 }
00926
00927 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
00928
00929
00930 if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
00931
00932
00933 if (GetCompany(v->owner)->engine_renew && GetEngine(v->engine_type)->company_avail != 0) return;
00934
00935 StringID str;
00936 if (age == -DAYS_IN_LEAP_YEAR) {
00937 str = STR_01A0_IS_GETTING_OLD;
00938 } else if (age == 0) {
00939 str = STR_01A1_IS_GETTING_VERY_OLD;
00940 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
00941 str = STR_01A2_IS_GETTING_VERY_OLD_AND;
00942 } else {
00943 return;
00944 }
00945
00946 SetDParam(0, v->index);
00947 AddNewsItem(str, NS_ADVICE, v->index, 0);
00948 }
00949
00956 uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *colour)
00957 {
00958 int count = 0;
00959 int max = 0;
00960 int cars = 0;
00961 int unloading = 0;
00962 bool loading = false;
00963
00964 const Vehicle *u = v;
00965 const Station *st = v->last_station_visited != INVALID_STATION ? GetStation(v->last_station_visited) : NULL;
00966
00967
00968 for (; v != NULL; v = v->Next()) {
00969 count += v->cargo.Count();
00970 max += v->cargo_cap;
00971 if (v->cargo_cap != 0 && colour != NULL) {
00972 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
00973 loading |= !(u->current_order.GetLoadType() & OLFB_NO_LOAD) && st->goods[v->cargo_type].days_since_pickup != 255;
00974 cars++;
00975 }
00976 }
00977
00978 if (colour != NULL) {
00979 if (unloading == 0 && loading) {
00980 *colour = STR_PERCENT_UP;
00981 } else if (cars == unloading || !loading) {
00982 *colour = STR_PERCENT_DOWN;
00983 } else {
00984 *colour = STR_PERCENT_UP_DOWN;
00985 }
00986 }
00987
00988
00989 if (max == 0) return 100;
00990
00991
00992 return (count * 100) / max;
00993 }
00994
00995 void VehicleEnterDepot(Vehicle *v)
00996 {
00997 switch (v->type) {
00998 case VEH_TRAIN:
00999 InvalidateWindowClasses(WC_TRAINS_LIST);
01000
01001 SetDepotWaypointReservation(v->tile, false);
01002 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(v->tile);
01003
01004 if (!IsFrontEngine(v)) v = v->First();
01005 UpdateSignalsOnSegment(v->tile, INVALID_DIAGDIR, v->owner);
01006 v->load_unload_time_rem = 0;
01007 ClrBit(v->u.rail.flags, VRF_TOGGLE_REVERSE);
01008 TrainConsistChanged(v, true);
01009 break;
01010
01011 case VEH_ROAD:
01012 InvalidateWindowClasses(WC_ROADVEH_LIST);
01013 if (!IsRoadVehFront(v)) v = v->First();
01014 break;
01015
01016 case VEH_SHIP:
01017 InvalidateWindowClasses(WC_SHIPS_LIST);
01018 v->u.ship.state = TRACK_BIT_DEPOT;
01019 RecalcShipStuff(v);
01020 break;
01021
01022 case VEH_AIRCRAFT:
01023 InvalidateWindowClasses(WC_AIRCRAFT_LIST);
01024 HandleAircraftEnterHangar(v);
01025 break;
01026 default: NOT_REACHED();
01027 }
01028
01029 if (v->type != VEH_TRAIN) {
01030
01031
01032 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01033 }
01034 InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
01035
01036 v->vehstatus |= VS_HIDDEN;
01037 v->cur_speed = 0;
01038
01039 VehicleServiceInDepot(v);
01040
01041 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01042
01043 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01044 InvalidateWindow(WC_VEHICLE_VIEW, v->index);
01045
01046 const Order *real_order = GetVehicleOrder(v, v->cur_order_index);
01047 Order t = v->current_order;
01048 v->current_order.MakeDummy();
01049
01050
01051
01052 if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
01053 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
01054 (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
01055
01056 return;
01057 }
01058
01059 if (t.IsRefit()) {
01060 _current_company = v->owner;
01061 CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
01062
01063 if (CmdFailed(cost)) {
01064 _vehicles_to_autoreplace[v] = false;
01065 if (v->owner == _local_company) {
01066
01067 SetDParam(0, v->index);
01068 AddNewsItem(STR_ORDER_REFIT_FAILED, NS_ADVICE, v->index, 0);
01069 }
01070 } else if (v->owner == _local_company && cost.GetCost() != 0) {
01071 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01072 }
01073 }
01074
01075 if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01076
01077 UpdateVehicleTimetable(v, true);
01078 v->cur_order_index++;
01079 }
01080 if (t.GetDepotActionType() & ODATFB_HALT) {
01081
01082 _vehicles_to_autoreplace[v] = false;
01083 if (v->owner == _local_company) {
01084 StringID string;
01085
01086 switch (v->type) {
01087 case VEH_TRAIN: string = STR_8814_TRAIN_IS_WAITING_IN_DEPOT; break;
01088 case VEH_ROAD: string = STR_9016_ROAD_VEHICLE_IS_WAITING; break;
01089 case VEH_SHIP: string = STR_981C_SHIP_IS_WAITING_IN_DEPOT; break;
01090 case VEH_AIRCRAFT: string = STR_A014_AIRCRAFT_IS_WAITING_IN; break;
01091 default: NOT_REACHED(); string = STR_EMPTY;
01092 }
01093
01094 SetDParam(0, v->index);
01095 AddNewsItem(string, NS_ADVICE, v->index, 0);
01096 }
01097 AI::NewEvent(v->owner, new AIEventVehicleWaitingInDepot(v->index));
01098 }
01099 }
01100 }
01101
01102
01110 void VehicleMove(Vehicle *v, bool update_viewport)
01111 {
01112 int img = v->cur_image;
01113 Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
01114 const Sprite *spr = GetSprite(img, ST_NORMAL);
01115
01116 pt.x += spr->x_offs;
01117 pt.y += spr->y_offs;
01118
01119 UpdateVehiclePosHash(v, pt.x, pt.y);
01120
01121 Rect old_coord = v->coord;
01122 v->coord.left = pt.x;
01123 v->coord.top = pt.y;
01124 v->coord.right = pt.x + spr->width + 2;
01125 v->coord.bottom = pt.y + spr->height + 2;
01126
01127 if (update_viewport) {
01128 MarkAllViewportsDirty(
01129 min(old_coord.left, v->coord.left),
01130 min(old_coord.top, v->coord.top),
01131 max(old_coord.right, v->coord.right) + 1,
01132 max(old_coord.bottom, v->coord.bottom) + 1
01133 );
01134 }
01135 }
01136
01145 void MarkSingleVehicleDirty(const Vehicle *v)
01146 {
01147 MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1, v->coord.bottom + 1);
01148 }
01149
01154 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01155 {
01156 static const int8 _delta_coord[16] = {
01157 -1,-1,-1, 0, 1, 1, 1, 0,
01158 -1, 0, 1, 1, 1, 0,-1,-1,
01159 };
01160
01161 int x = v->x_pos + _delta_coord[v->direction];
01162 int y = v->y_pos + _delta_coord[v->direction + 8];
01163
01164 GetNewVehiclePosResult gp;
01165 gp.x = x;
01166 gp.y = y;
01167 gp.old_tile = v->tile;
01168 gp.new_tile = TileVirtXY(x, y);
01169 return gp;
01170 }
01171
01172 static const Direction _new_direction_table[] = {
01173 DIR_N , DIR_NW, DIR_W ,
01174 DIR_NE, DIR_SE, DIR_SW,
01175 DIR_E , DIR_SE, DIR_S
01176 };
01177
01178 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01179 {
01180 int i = 0;
01181
01182 if (y >= v->y_pos) {
01183 if (y != v->y_pos) i += 3;
01184 i += 3;
01185 }
01186
01187 if (x >= v->x_pos) {
01188 if (x != v->x_pos) i++;
01189 i++;
01190 }
01191
01192 Direction dir = v->direction;
01193
01194 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01195 if (dirdiff == DIRDIFF_SAME) return dir;
01196 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01197 }
01198
01199 Trackdir GetVehicleTrackdir(const Vehicle *v)
01200 {
01201 if (v->vehstatus & VS_CRASHED) return INVALID_TRACKDIR;
01202
01203 switch (v->type) {
01204 case VEH_TRAIN:
01205 if (v->u.rail.track == TRACK_BIT_DEPOT)
01206 return DiagDirToDiagTrackdir(GetRailDepotDirection(v->tile));
01207
01208 if (v->u.rail.track == TRACK_BIT_WORMHOLE)
01209 return DiagDirToDiagTrackdir(DirToDiagDir(v->direction));
01210
01211 return TrackDirectionToTrackdir(FindFirstTrack(v->u.rail.track), v->direction);
01212
01213 case VEH_SHIP:
01214 if (v->IsInDepot())
01215
01216 return DiagDirToDiagTrackdir(GetShipDepotDirection(v->tile));
01217
01218 if (v->u.ship.state == TRACK_BIT_WORMHOLE)
01219 return DiagDirToDiagTrackdir(DirToDiagDir(v->direction));
01220
01221 return TrackDirectionToTrackdir(FindFirstTrack(v->u.ship.state), v->direction);
01222
01223 case VEH_ROAD:
01224 if (v->IsInDepot())
01225 return DiagDirToDiagTrackdir(GetRoadDepotDirection(v->tile));
01226
01227 if (IsStandardRoadStopTile(v->tile))
01228 return DiagDirToDiagTrackdir(GetRoadStopDir(v->tile));
01229
01230
01231 if (v->u.road.state > RVSB_TRACKDIR_MASK) return DiagDirToDiagTrackdir(DirToDiagDir(v->direction));
01232
01233
01234
01235 return (Trackdir)((IsReversingRoadTrackdir((Trackdir)v->u.road.state)) ? (v->u.road.state - 6) : v->u.road.state);
01236
01237
01238 default: return INVALID_TRACKDIR;
01239 }
01240 }
01241
01251 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01252 {
01253 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01254 }
01255
01256 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01257 {
01258
01259 const Vehicle *v;
01260 FOR_ALL_VEHICLES(v) {
01261 if (v->type == type && v->owner == owner) {
01262 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01263 }
01264 }
01265
01266 if (this->maxid == 0) return;
01267
01268 this->maxid++;
01269 this->maxid++;
01270
01271 this->cache = CallocT<bool>(this->maxid);
01272
01273
01274 FOR_ALL_VEHICLES(v) {
01275 if (v->type == type && v->owner == owner) {
01276 this->cache[v->unitnumber] = true;
01277 }
01278 }
01279 }
01280
01281 UnitID FreeUnitIDGenerator::NextID()
01282 {
01283 if (this->maxid <= this->curid) return ++this->curid;
01284
01285 while (this->cache[++this->curid]) { }
01286
01287 return this->curid;
01288 }
01289
01290 UnitID GetFreeUnitNumber(VehicleType type)
01291 {
01292 FreeUnitIDGenerator gen(type, _current_company);
01293
01294 return gen.NextID();
01295 }
01296
01297
01306 bool CanBuildVehicleInfrastructure(VehicleType type)
01307 {
01308 assert(IsCompanyBuildableVehicleType(type));
01309
01310 if (!IsValidCompanyID(_local_company)) return false;
01311 if (_settings_client.gui.always_build_infrastructure) return true;
01312
01313 UnitID max;
01314 switch (type) {
01315 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
01316 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
01317 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
01318 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01319 default: NOT_REACHED();
01320 }
01321
01322
01323 if (max > 0) {
01324
01325 const Engine *e;
01326 FOR_ALL_ENGINES_OF_TYPE(e, type) {
01327 if (HasBit(e->company_avail, _local_company)) return true;
01328 }
01329 return false;
01330 }
01331
01332
01333 const Vehicle *v;
01334 FOR_ALL_VEHICLES(v) {
01335 if (v->owner == _local_company && v->type == type) return true;
01336 }
01337
01338 return false;
01339 }
01340
01341
01342 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01343 {
01344 const Company *c = GetCompany(company);
01345 LiveryScheme scheme = LS_DEFAULT;
01346 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01347
01348
01349
01350 if (c->livery[LS_DEFAULT].in_use && (_settings_client.gui.liveries == 2 || (_settings_client.gui.liveries == 1 && company == _local_company))) {
01351
01352 const Engine *e = GetEngine(engine_type);
01353 switch (e->type) {
01354 default: NOT_REACHED();
01355 case VEH_TRAIN: {
01356 const RailVehicleInfo *rvi = RailVehInfo(engine_type);
01357 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (IsArticulatedPart(v) && rvi->railveh_type != RAILVEH_WAGON))) {
01358
01359
01360 engine_type = parent_engine_type;
01361 e = GetEngine(engine_type);
01362 rvi = RailVehInfo(engine_type);
01363
01364 }
01365
01366 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01367 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01368 if (rvi->railveh_type == RAILVEH_WAGON) {
01369 if (!GetCargo(cargo_type)->is_freight) {
01370 if (parent_engine_type == INVALID_ENGINE) {
01371 scheme = LS_PASSENGER_WAGON_STEAM;
01372 } else {
01373 switch (RailVehInfo(parent_engine_type)->engclass) {
01374 default: NOT_REACHED();
01375 case EC_STEAM: scheme = LS_PASSENGER_WAGON_STEAM; break;
01376 case EC_DIESEL: scheme = LS_PASSENGER_WAGON_DIESEL; break;
01377 case EC_ELECTRIC: scheme = LS_PASSENGER_WAGON_ELECTRIC; break;
01378 case EC_MONORAIL: scheme = LS_PASSENGER_WAGON_MONORAIL; break;
01379 case EC_MAGLEV: scheme = LS_PASSENGER_WAGON_MAGLEV; break;
01380 }
01381 }
01382 } else {
01383 scheme = LS_FREIGHT_WAGON;
01384 }
01385 } else {
01386 bool is_mu = HasBit(EngInfo(engine_type)->misc_flags, EF_RAIL_IS_MU);
01387
01388 switch (rvi->engclass) {
01389 default: NOT_REACHED();
01390 case EC_STEAM: scheme = LS_STEAM; break;
01391 case EC_DIESEL: scheme = is_mu ? LS_DMU : LS_DIESEL; break;
01392 case EC_ELECTRIC: scheme = is_mu ? LS_EMU : LS_ELECTRIC; break;
01393 case EC_MONORAIL: scheme = LS_MONORAIL; break;
01394 case EC_MAGLEV: scheme = LS_MAGLEV; break;
01395 }
01396 }
01397 break;
01398 }
01399
01400 case VEH_ROAD: {
01401
01402 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01403 engine_type = parent_engine_type;
01404 e = GetEngine(engine_type);
01405 cargo_type = v->First()->cargo_type;
01406 }
01407 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01408 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01409
01410
01411 if (HasBit(EngInfo(engine_type)->misc_flags, EF_ROAD_TRAM)) {
01412
01413 scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01414 } else {
01415
01416 scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01417 }
01418 break;
01419 }
01420
01421 case VEH_SHIP: {
01422 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01423 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01424 scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01425 break;
01426 }
01427
01428 case VEH_AIRCRAFT: {
01429 switch (e->u.air.subtype) {
01430 case AIR_HELI: scheme = LS_HELICOPTER; break;
01431 case AIR_CTOL: scheme = LS_SMALL_PLANE; break;
01432 case AIR_CTOL | AIR_FAST: scheme = LS_LARGE_PLANE; break;
01433 }
01434 break;
01435 }
01436 }
01437
01438
01439 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01440 }
01441
01442 return &c->livery[scheme];
01443 }
01444
01445
01446 static SpriteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01447 {
01448 SpriteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01449
01450
01451 if (map != PAL_NONE) return map;
01452
01453
01454 if (HasBit(EngInfo(engine_type)->callbackmask, CBM_VEHICLE_COLOUR_REMAP)) {
01455 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01456
01457
01458 if (callback != CALLBACK_FAILED && callback != 0xC000) {
01459 map = GB(callback, 0, 14);
01460
01461
01462 if (!HasBit(callback, 14)) {
01463
01464 if (v != NULL) ((Vehicle*)v)->colourmap = map;
01465 return map;
01466 }
01467 }
01468 }
01469
01470 bool twocc = HasBit(EngInfo(engine_type)->misc_flags, EF_USES_2CC);
01471
01472 if (map == PAL_NONE) map = twocc ? (SpriteID)SPR_2CCMAP_BASE : (SpriteID)PALETTE_RECOLOUR_START;
01473
01474 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v);
01475
01476 map += livery->colour1;
01477 if (twocc) map += livery->colour2 * 16;
01478
01479
01480 if (v != NULL) ((Vehicle*)v)->colourmap = map;
01481 return map;
01482 }
01483
01484 SpriteID GetEnginePalette(EngineID engine_type, CompanyID company)
01485 {
01486 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
01487 }
01488
01489 SpriteID GetVehiclePalette(const Vehicle *v)
01490 {
01491 if (v->type == VEH_TRAIN) {
01492 return GetEngineColourMap(v->engine_type, v->owner, v->u.rail.first_engine, v);
01493 } else if (v->type == VEH_ROAD) {
01494 return GetEngineColourMap(v->engine_type, v->owner, v->u.road.first_engine, v);
01495 }
01496
01497 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
01498 }
01499
01500
01501 void Vehicle::BeginLoading()
01502 {
01503 assert(IsTileType(tile, MP_STATION) || type == VEH_SHIP);
01504
01505 if (this->current_order.IsType(OT_GOTO_STATION) &&
01506 this->current_order.GetDestination() == this->last_station_visited) {
01507 current_order.MakeLoading(true);
01508 UpdateVehicleTimetable(this, true);
01509
01510
01511
01512
01513
01514
01515 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
01516
01517 } else {
01518 current_order.MakeLoading(false);
01519 }
01520
01521 GetStation(this->last_station_visited)->loading_vehicles.push_back(this);
01522
01523 PrepareUnload(this);
01524
01525 InvalidateWindow(GetWindowClassForVehicleType(this->type), this->owner);
01526 InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01527 InvalidateWindow(WC_VEHICLE_DETAILS, this->index);
01528 InvalidateWindow(WC_STATION_VIEW, this->last_station_visited);
01529
01530 GetStation(this->last_station_visited)->MarkTilesDirty(true);
01531 this->cur_speed = 0;
01532 this->MarkDirty();
01533 }
01534
01535 void Vehicle::LeaveStation()
01536 {
01537 assert(current_order.IsType(OT_LOADING));
01538
01539 delete this->cargo_payment;
01540
01541
01542 if (current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
01543
01544 current_order.MakeLeaveStation();
01545 Station *st = GetStation(this->last_station_visited);
01546 st->loading_vehicles.remove(this);
01547
01548 HideFillingPercent(&this->fill_percent_te_id);
01549
01550 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
01551
01552 if (IsTileType(this->tile, MP_STATION)) StationAnimationTrigger(st, this->tile, STAT_ANIM_TRAIN_DEPARTS);
01553
01554
01555
01556
01557 if (UpdateSignalsOnSegment(this->tile, TrackdirToExitdir(GetVehicleTrackdir(this)), this->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) {
01558 TryPathReserve(this, true, true);
01559 }
01560 }
01561 }
01562
01563
01564 void Vehicle::HandleLoading(bool mode)
01565 {
01566 switch (this->current_order.GetType()) {
01567 case OT_LOADING: {
01568 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
01569
01570
01571 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) ||
01572 (_settings_game.order.timetabling && this->current_order_time < wait_time)) return;
01573
01574 this->PlayLeaveStationSound();
01575
01576 bool at_destination_station = this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE;
01577 this->LeaveStation();
01578
01579
01580 if (!at_destination_station) return;
01581 break;
01582 }
01583
01584 case OT_DUMMY: break;
01585
01586 default: return;
01587 }
01588
01589 this->cur_order_index++;
01590 InvalidateVehicleOrder(this, 0);
01591 }
01592
01593 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
01594 {
01595 if (!CheckOwnership(this->owner)) return CMD_ERROR;
01596 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
01597 if (this->IsStoppedInDepot()) return CMD_ERROR;
01598
01599 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
01600 bool halt_in_depot = this->current_order.GetDepotActionType() & ODATFB_HALT;
01601 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
01602
01603
01604
01605 if (flags & DC_EXEC) {
01606 this->current_order.SetDepotOrderType(ODTF_MANUAL);
01607 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
01608 InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01609 }
01610 return CommandCost();
01611 }
01612
01613 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR;
01614 if (flags & DC_EXEC) {
01615
01616
01617 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->cur_order_index++;
01618
01619 this->current_order.MakeDummy();
01620 InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01621 }
01622 return CommandCost();
01623 }
01624
01625 TileIndex location;
01626 DestinationID destination;
01627 bool reverse;
01628 static const StringID no_depot[] = {STR_883A_UNABLE_TO_FIND_ROUTE_TO, STR_9019_UNABLE_TO_FIND_LOCAL_DEPOT, STR_981A_UNABLE_TO_FIND_LOCAL_DEPOT, STR_A012_CAN_T_SEND_AIRCRAFT_TO};
01629 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
01630
01631 if (flags & DC_EXEC) {
01632 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
01633
01634 this->dest_tile = location;
01635 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
01636 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
01637 InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
01638
01639
01640 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
01641
01642 if (this->type == VEH_AIRCRAFT && this->u.air.state == FLYING && this->u.air.targetairport != destination) {
01643
01644 extern void AircraftNextAirportPos_and_Order(Vehicle *v);
01645 AircraftNextAirportPos_and_Order(this);
01646 }
01647 }
01648
01649 return CommandCost();
01650
01651 }
01652
01653 void Vehicle::SetNext(Vehicle *next)
01654 {
01655 if (this->next != NULL) {
01656
01657 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
01658 v->first = this->next;
01659 }
01660 this->next->previous = NULL;
01661 }
01662
01663 this->next = next;
01664
01665 if (this->next != NULL) {
01666
01667 if (this->next->previous != NULL) this->next->previous->next = NULL;
01668 this->next->previous = this;
01669 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
01670 v->first = this->first;
01671 }
01672 }
01673 }
01674
01675 void Vehicle::AddToShared(Vehicle *shared_chain)
01676 {
01677 assert(this->previous_shared == NULL && this->next_shared == NULL);
01678
01679 if (!shared_chain->orders.list) {
01680 assert(shared_chain->previous_shared == NULL);
01681 assert(shared_chain->next_shared == NULL);
01682 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
01683 }
01684
01685 this->next_shared = shared_chain->next_shared;
01686 this->previous_shared = shared_chain;
01687
01688 shared_chain->next_shared = this;
01689
01690 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
01691
01692 shared_chain->orders.list->AddVehicle(this);
01693 }
01694
01695 void Vehicle::RemoveFromShared()
01696 {
01697
01698
01699 bool were_first = (this->FirstShared() == this);
01700 uint32 old_window_number = (this->FirstShared()->index << 16) | (this->type << 11) | VLW_SHARED_ORDERS | this->owner;
01701
01702 this->orders.list->RemoveVehicle(this);
01703
01704 if (!were_first) {
01705
01706 this->previous_shared->next_shared = this->NextShared();
01707 }
01708
01709 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
01710
01711
01712 if (this->orders.list->GetNumVehicles() == 1) {
01713
01714 DeleteWindowById(GetWindowClassForVehicleType(this->type), old_window_number);
01715 InvalidateVehicleOrder(this->FirstShared(), 0);
01716 } else if (were_first) {
01717
01718
01719 InvalidateWindowData(GetWindowClassForVehicleType(this->type), old_window_number, (this->FirstShared()->index << 16) | (1 << 15));
01720 }
01721
01722 this->next_shared = NULL;
01723 this->previous_shared = NULL;
01724 }
01725
01726 void StopAllVehicles()
01727 {
01728 Vehicle *v;
01729 FOR_ALL_VEHICLES(v) {
01730
01731
01732 v->vehstatus |= VS_STOPPED;
01733 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
01734 InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
01735 }
01736 }
01737
01738 void VehiclesYearlyLoop()
01739 {
01740 Vehicle *v;
01741 FOR_ALL_VEHICLES(v) {
01742 if (v->IsPrimaryVehicle()) {
01743
01744 Money profit = v->GetDisplayProfitThisYear();
01745 if (v->age >= 730 && profit < 0) {
01746 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
01747 SetDParam(0, v->index);
01748 SetDParam(1, profit);
01749 AddNewsItem(
01750 STR_VEHICLE_IS_UNPROFITABLE,
01751 NS_ADVICE,
01752 v->index,
01753 0);
01754 }
01755 AI::NewEvent(v->owner, new AIEventVehicleUnprofitable(v->index));
01756 }
01757
01758 v->profit_last_year = v->profit_this_year;
01759 v->profit_this_year = 0;
01760 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
01761 }
01762 }
01763 }
01764
01765
01775 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
01776 {
01777 assert(IsEngineIndex(engine_type));
01778 const Engine *e = GetEngine(engine_type);
01779
01780 switch (e->type) {
01781 case VEH_TRAIN:
01782 return (st->facilities & FACIL_TRAIN) != 0;
01783
01784 case VEH_ROAD:
01785
01786
01787
01788 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
01789
01790 case VEH_SHIP:
01791 return (st->facilities & FACIL_DOCK) != 0;
01792
01793 case VEH_AIRCRAFT:
01794 return (st->facilities & FACIL_AIRPORT) != 0 &&
01795 (st->Airport()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
01796
01797 default:
01798 return false;
01799 }
01800 }
01801
01808 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
01809 {
01810 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(v) != NULL;
01811
01812 return CanVehicleUseStation(v->engine_type, st);
01813 }