00001
00002
00005 #include "stdafx.h"
00006 #include "openttd.h"
00007 #include "roadveh.h"
00008 #include "ship.h"
00009 #include "spritecache.h"
00010 #include "landscape.h"
00011 #include "timetable.h"
00012 #include "viewport_func.h"
00013 #include "gfx_func.h"
00014 #include "news_func.h"
00015 #include "command_func.h"
00016 #include "company_func.h"
00017 #include "vehicle_gui.h"
00018 #include "train.h"
00019 #include "aircraft.h"
00020 #include "newgrf_engine.h"
00021 #include "newgrf_sound.h"
00022 #include "newgrf_station.h"
00023 #include "newgrf_text.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 "string_func.h"
00035 #include "oldpool_func.h"
00036 #include "depot_map.h"
00037 #include "ai/ai.hpp"
00038 #include "core/smallmap_type.hpp"
00039 #include "vehiclelist.h"
00040 #include "depot_func.h"
00041 #include "settings_type.h"
00042
00043 #include "table/sprites.h"
00044 #include "table/strings.h"
00045
00046 #define GEN_HASH(x, y) ((GB((y), 6, 6) << 6) + GB((x), 7, 6))
00047
00048 VehicleID _vehicle_id_ctr_day;
00049 const Vehicle *_place_clicked_vehicle;
00050 VehicleID _new_vehicle_id;
00051 uint16 _returned_refit_capacity;
00052
00053
00054
00055 const uint32 _veh_build_proc_table[] = {
00056 CMD_BUILD_RAIL_VEHICLE,
00057 CMD_BUILD_ROAD_VEH,
00058 CMD_BUILD_SHIP,
00059 CMD_BUILD_AIRCRAFT,
00060 };
00061 const uint32 _veh_sell_proc_table[] = {
00062 CMD_SELL_RAIL_WAGON,
00063 CMD_SELL_ROAD_VEH,
00064 CMD_SELL_SHIP,
00065 CMD_SELL_AIRCRAFT,
00066 };
00067
00068 const uint32 _veh_refit_proc_table[] = {
00069 CMD_REFIT_RAIL_VEHICLE,
00070 CMD_REFIT_ROAD_VEH,
00071 CMD_REFIT_SHIP,
00072 CMD_REFIT_AIRCRAFT,
00073 };
00074
00075 const uint32 _send_to_depot_proc_table[] = {
00076 CMD_SEND_TRAIN_TO_DEPOT,
00077 CMD_SEND_ROADVEH_TO_DEPOT,
00078 CMD_SEND_SHIP_TO_DEPOT,
00079 CMD_SEND_AIRCRAFT_TO_HANGAR,
00080 };
00081
00082
00083
00084 DEFINE_OLD_POOL_GENERIC(Vehicle, Vehicle)
00085
00086
00090 bool Vehicle::NeedsAutorenewing(const Company *c) const
00091 {
00092
00093
00094
00095
00096 assert(c == GetCompany(this->owner));
00097
00098 if (!c->engine_renew) return false;
00099 if (this->age - this->max_age < (c->engine_renew_months * 30)) return false;
00100 if (this->age == 0) return false;
00101
00102 return true;
00103 }
00104
00105 void VehicleServiceInDepot(Vehicle *v)
00106 {
00107 v->date_of_last_service = _date;
00108 v->breakdowns_since_last_service = 0;
00109 v->reliability = GetEngine(v->engine_type)->reliability;
00110 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
00111 }
00112
00113 bool Vehicle::NeedsServicing() const
00114 {
00115 if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
00116
00117 if (_settings_game.order.no_servicing_if_no_breakdowns && _settings_game.difficulty.vehicle_breakdowns == 0) {
00118
00119
00120 return EngineHasReplacementForCompany(GetCompany(this->owner), this->engine_type, this->group_id);
00121 }
00122
00123 return _settings_game.vehicle.servint_ispercent ?
00124 (this->reliability < GetEngine(this->engine_type)->reliability * (100 - this->service_interval) / 100) :
00125 (this->date_of_last_service + this->service_interval < _date);
00126 }
00127
00128 bool Vehicle::NeedsAutomaticServicing() const
00129 {
00130 if (_settings_game.order.gotodepot && VehicleHasDepotOrders(this)) return false;
00131 if (this->current_order.IsType(OT_LOADING)) return false;
00132 if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
00133 return NeedsServicing();
00134 }
00135
00136 StringID VehicleInTheWayErrMsg(const Vehicle *v)
00137 {
00138 switch (v->type) {
00139 case VEH_TRAIN: return STR_8803_TRAIN_IN_THE_WAY;
00140 case VEH_ROAD: return STR_9000_ROAD_VEHICLE_IN_THE_WAY;
00141 case VEH_AIRCRAFT: return STR_A015_AIRCRAFT_IN_THE_WAY;
00142 default: return STR_980E_SHIP_IN_THE_WAY;
00143 }
00144 }
00145
00146 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
00147 {
00148 byte z = *(byte*)data;
00149
00150 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
00151 if (v->z_pos > z) return NULL;
00152
00153 _error_message = VehicleInTheWayErrMsg(v);
00154 return v;
00155 }
00156
00157 bool EnsureNoVehicleOnGround(TileIndex tile)
00158 {
00159 byte z = GetTileMaxZ(tile);
00160 return !HasVehicleOnPos(tile, &z, &EnsureNoVehicleProcZ);
00161 }
00162
00164 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
00165 {
00166 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
00167 if (v == (const Vehicle *)data) return NULL;
00168
00169 _error_message = VehicleInTheWayErrMsg(v);
00170 return v;
00171 }
00172
00180 bool HasVehicleOnTunnelBridge(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
00181 {
00182 return HasVehicleOnPos(tile, (void *)ignore, &GetVehicleTunnelBridgeProc) ||
00183 HasVehicleOnPos(endtile, (void *)ignore, &GetVehicleTunnelBridgeProc);
00184 }
00185
00186
00187 static void UpdateVehiclePosHash(Vehicle *v, int x, int y);
00188
00189 void VehiclePositionChanged(Vehicle *v)
00190 {
00191 int img = v->cur_image;
00192 Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
00193 const Sprite *spr = GetSprite(img, ST_NORMAL);
00194
00195 pt.x += spr->x_offs;
00196 pt.y += spr->y_offs;
00197
00198 UpdateVehiclePosHash(v, pt.x, pt.y);
00199
00200 v->left_coord = pt.x;
00201 v->top_coord = pt.y;
00202 v->right_coord = pt.x + spr->width + 2;
00203 v->bottom_coord = pt.y + spr->height + 2;
00204 }
00205
00206 Vehicle::Vehicle()
00207 {
00208 this->type = VEH_INVALID;
00209 this->left_coord = INVALID_COORD;
00210 this->group_id = DEFAULT_GROUP;
00211 this->fill_percent_te_id = INVALID_TE_ID;
00212 this->first = this;
00213 this->colourmap = PAL_NONE;
00214 }
00215
00220 byte VehicleRandomBits()
00221 {
00222 return GB(Random(), 0, 8);
00223 }
00224
00225
00226 bool Vehicle::AllocateList(Vehicle **vl, int num)
00227 {
00228 if (!Vehicle::CanAllocateItem(num)) return false;
00229 if (vl == NULL) return true;
00230
00231 uint counter = _Vehicle_pool.first_free_index;
00232
00233 for (int i = 0; i != num; i++) {
00234 vl[i] = new (AllocateRaw(counter)) InvalidVehicle();
00235 counter++;
00236 }
00237
00238 return true;
00239 }
00240
00241
00242
00243 const int HASH_BITS = 7;
00244 const int HASH_SIZE = 1 << HASH_BITS;
00245 const int HASH_MASK = HASH_SIZE - 1;
00246 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
00247 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
00248
00249
00250
00251 const int HASH_RES = 0;
00252
00253 static Vehicle *_new_vehicle_position_hash[TOTAL_HASH_SIZE];
00254
00255 static Vehicle *VehicleFromHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
00256 {
00257 for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
00258 for (int x = xl; ; x = (x + 1) & HASH_MASK) {
00259 Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00260 for (; v != NULL; v = v->next_new_hash) {
00261 Vehicle *a = proc(v, data);
00262 if (find_first && a != NULL) return a;
00263 }
00264 if (x == xu) break;
00265 }
00266 if (y == yu) break;
00267 }
00268
00269 return NULL;
00270 }
00271
00272
00284 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
00285 {
00286 const int COLL_DIST = 6;
00287
00288
00289 int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00290 int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00291 int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00292 int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00293
00294 return VehicleFromHash(xl, yl, xu, yu, data, proc, find_first);
00295 }
00296
00311 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00312 {
00313 VehicleFromPosXY(x, y, data, proc, false);
00314 }
00315
00327 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00328 {
00329 return VehicleFromPosXY(x, y, data, proc, true) != NULL;
00330 }
00331
00342 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
00343 {
00344 int x = GB(TileX(tile), HASH_RES, HASH_BITS);
00345 int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
00346
00347 Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00348 for (; v != NULL; v = v->next_new_hash) {
00349 if (v->tile != tile) continue;
00350
00351 Vehicle *a = proc(v, data);
00352 if (find_first && a != NULL) return a;
00353 }
00354
00355 return NULL;
00356 }
00357
00371 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00372 {
00373 VehicleFromPos(tile, data, proc, false);
00374 }
00375
00386 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00387 {
00388 return VehicleFromPos(tile, data, proc, true) != NULL;
00389 }
00390
00391
00392 static void UpdateNewVehiclePosHash(Vehicle *v, bool remove)
00393 {
00394 Vehicle **old_hash = v->old_new_hash;
00395 Vehicle **new_hash;
00396
00397 if (remove) {
00398 new_hash = NULL;
00399 } else {
00400 int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
00401 int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
00402 new_hash = &_new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00403 }
00404
00405 if (old_hash == new_hash) return;
00406
00407
00408 if (old_hash != NULL) {
00409 Vehicle *last = NULL;
00410 Vehicle *u = *old_hash;
00411 while (u != v) {
00412 last = u;
00413 u = u->next_new_hash;
00414 assert(u != NULL);
00415 }
00416
00417 if (last == NULL) {
00418 *old_hash = v->next_new_hash;
00419 } else {
00420 last->next_new_hash = v->next_new_hash;
00421 }
00422 }
00423
00424
00425 if (new_hash != NULL) {
00426 v->next_new_hash = *new_hash;
00427 *new_hash = v;
00428 assert(v != v->next_new_hash);
00429 }
00430
00431
00432 v->old_new_hash = new_hash;
00433 }
00434
00435 static Vehicle *_vehicle_position_hash[0x1000];
00436
00437 static void UpdateVehiclePosHash(Vehicle *v, int x, int y)
00438 {
00439 UpdateNewVehiclePosHash(v, x == INVALID_COORD);
00440
00441 Vehicle **old_hash, **new_hash;
00442 int old_x = v->left_coord;
00443 int old_y = v->top_coord;
00444
00445 new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(x, y)];
00446 old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(old_x, old_y)];
00447
00448 if (old_hash == new_hash) return;
00449
00450
00451 if (old_hash != NULL) {
00452 Vehicle *last = NULL;
00453 Vehicle *u = *old_hash;
00454 while (u != v) {
00455 last = u;
00456 u = u->next_hash;
00457 assert(u != NULL);
00458 }
00459
00460 if (last == NULL) {
00461 *old_hash = v->next_hash;
00462 } else {
00463 last->next_hash = v->next_hash;
00464 }
00465 }
00466
00467
00468 if (new_hash != NULL) {
00469 v->next_hash = *new_hash;
00470 *new_hash = v;
00471 }
00472 }
00473
00474 void ResetVehiclePosHash()
00475 {
00476 Vehicle *v;
00477 FOR_ALL_VEHICLES(v) { v->old_new_hash = NULL; }
00478 memset(_vehicle_position_hash, 0, sizeof(_vehicle_position_hash));
00479 memset(_new_vehicle_position_hash, 0, sizeof(_new_vehicle_position_hash));
00480 }
00481
00482 void ResetVehicleColourMap()
00483 {
00484 Vehicle *v;
00485 FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
00486 }
00487
00492 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
00493 static AutoreplaceMap _vehicles_to_autoreplace;
00494
00495 void InitializeVehicles()
00496 {
00497 _Vehicle_pool.CleanPool();
00498 _Vehicle_pool.AddBlockToPool();
00499
00500 _vehicles_to_autoreplace.Reset();
00501 ResetVehiclePosHash();
00502 }
00503
00504 Vehicle *GetLastVehicleInChain(Vehicle *v)
00505 {
00506 while (v->Next() != NULL) v = v->Next();
00507 return v;
00508 }
00509
00510 const Vehicle *GetLastVehicleInChain(const Vehicle *v)
00511 {
00512 while (v->Next() != NULL) v = v->Next();
00513 return v;
00514 }
00515
00516 uint CountVehiclesInChain(const Vehicle *v)
00517 {
00518 uint count = 0;
00519 do count++; while ((v = v->Next()) != NULL);
00520 return count;
00521 }
00522
00527 bool IsEngineCountable(const Vehicle *v)
00528 {
00529 switch (v->type) {
00530 case VEH_AIRCRAFT: return IsNormalAircraft(v);
00531 case VEH_TRAIN:
00532 return !IsArticulatedPart(v) &&
00533 !IsRearDualheaded(v);
00534 case VEH_ROAD: return IsRoadVehFront(v);
00535 case VEH_SHIP: return true;
00536 default: return false;
00537 }
00538 }
00539
00540 void Vehicle::PreDestructor()
00541 {
00542 if (CleaningPool()) return;
00543
00544 if (IsValidStationID(this->last_station_visited)) {
00545 GetStation(this->last_station_visited)->loading_vehicles.remove(this);
00546
00547 HideFillingPercent(&this->fill_percent_te_id);
00548 }
00549
00550 if (IsEngineCountable(this)) {
00551 GetCompany(this->owner)->num_engines[this->engine_type]--;
00552 if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
00553
00554 DeleteGroupHighlightOfVehicle(this);
00555 if (IsValidGroupID(this->group_id)) GetGroup(this->group_id)->num_engines[this->engine_type]--;
00556 if (this->IsPrimaryVehicle()) DecreaseGroupNumVehicle(this->group_id);
00557 }
00558
00559 if (this->type == VEH_ROAD) ClearSlot(this);
00560 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
00561 Station *st = GetTargetAirportIfValid(this);
00562 if (st != NULL) {
00563 const AirportFTA *layout = st->Airport()->layout;
00564 CLRBITS(st->airport_flags, layout[this->u.air.previous_pos].block | layout[this->u.air.pos].block);
00565 }
00566 }
00567
00568 if (this->type != VEH_TRAIN || (this->type == VEH_TRAIN && (IsFrontEngine(this) || IsFreeWagon(this)))) {
00569 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
00570 }
00571
00572 if (this->IsPrimaryVehicle()) {
00573 DeleteWindowById(WC_VEHICLE_VIEW, this->index);
00574 DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
00575 DeleteWindowById(WC_VEHICLE_REFIT, this->index);
00576 DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
00577 DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
00578 InvalidateWindow(WC_COMPANY, this->owner);
00579 }
00580 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00581
00582 this->cargo.Truncate(0);
00583 DeleteVehicleOrders(this);
00584 DeleteDepotHighlightOfVehicle(this);
00585
00586 extern void StopGlobalFollowVehicle(const Vehicle *v);
00587 StopGlobalFollowVehicle(this);
00588 }
00589
00590 Vehicle::~Vehicle()
00591 {
00592 free(this->name);
00593
00594 if (CleaningPool()) return;
00595
00596
00597
00598 if (!(this->vehstatus & VS_HIDDEN)) MarkSingleVehicleDirty(this);
00599
00600 Vehicle *v = this->Next();
00601 this->SetNext(NULL);
00602
00603 delete v;
00604
00605 UpdateVehiclePosHash(this, INVALID_COORD, 0);
00606 this->next_hash = NULL;
00607 this->next_new_hash = NULL;
00608
00609 DeleteVehicleNews(this->index, INVALID_STRING_ID);
00610
00611 this->type = VEH_INVALID;
00612 }
00613
00617 void VehicleEnteredDepotThisTick(Vehicle *v)
00618 {
00619
00620
00621 _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED) &&
00622 (!v->current_order.IsType(OT_GOTO_DEPOT) ||
00623 !(v->current_order.GetDepotActionType() & ODATFB_HALT));
00624
00625
00626
00627
00628
00629
00630 v->vehstatus |= VS_STOPPED;
00631 }
00632
00633 void CallVehicleTicks()
00634 {
00635 _vehicles_to_autoreplace.Clear();
00636
00637 Station *st;
00638 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
00639
00640 Vehicle *v;
00641 FOR_ALL_VEHICLES(v) {
00642 v->Tick();
00643
00644 switch (v->type) {
00645 default: break;
00646
00647 case VEH_TRAIN:
00648 case VEH_ROAD:
00649 case VEH_AIRCRAFT:
00650 case VEH_SHIP:
00651 if (v->type == VEH_TRAIN && IsTrainWagon(v)) continue;
00652 if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
00653 if (v->type == VEH_ROAD && !IsRoadVehFront(v)) continue;
00654
00655 v->motion_counter += (v->direction & 1) ? (v->cur_speed * 3) / 4 : v->cur_speed;
00656
00657 if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
00658
00659
00660 if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
00661 }
00662 }
00663
00664 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
00665 v = it->first;
00666
00667 _current_company = v->owner;
00668
00669
00670
00671
00672 if (it->second) v->vehstatus &= ~VS_STOPPED;
00673
00674
00675 int x = v->x_pos;
00676 int y = v->y_pos;
00677 int z = v->z_pos;
00678
00679 const Company *c = GetCompany(_current_company);
00680 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->engine_renew_money));
00681 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
00682 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->engine_renew_money));
00683
00684 if (!IsLocalCompany()) continue;
00685
00686 if (res.Succeeded()) {
00687 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
00688 continue;
00689 }
00690
00691 StringID error_message = res.GetErrorMessage();
00692 if (error_message == STR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
00693
00694 if (error_message == STR_0003_NOT_ENOUGH_CASH_REQUIRES) error_message = STR_AUTOREPLACE_MONEY_LIMIT;
00695
00696 StringID message;
00697 if (error_message == STR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
00698 message = error_message;
00699 } else {
00700 message = STR_VEHICLE_AUTORENEW_FAILED;
00701 }
00702
00703 SetDParam(0, v->index);
00704 SetDParam(1, error_message);
00705 AddNewsItem(message, NS_ADVICE, v->index, 0);
00706 }
00707
00708 _current_company = OWNER_NONE;
00709 }
00710
00716 bool CanRefitTo(EngineID engine_type, CargoID cid_to)
00717 {
00718 return HasBit(EngInfo(engine_type)->refit_mask, cid_to);
00719 }
00720
00725 CargoID FindFirstRefittableCargo(EngineID engine_type)
00726 {
00727 uint32 refit_mask = EngInfo(engine_type)->refit_mask;
00728
00729 if (refit_mask != 0) {
00730 for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
00731 if (HasBit(refit_mask, cid)) return cid;
00732 }
00733 }
00734
00735 return CT_INVALID;
00736 }
00737
00742 CommandCost GetRefitCost(EngineID engine_type)
00743 {
00744 Money base_cost;
00745 ExpensesType expense_type;
00746 switch (GetEngine(engine_type)->type) {
00747 case VEH_SHIP:
00748 base_cost = _price.ship_base;
00749 expense_type = EXPENSES_SHIP_RUN;
00750 break;
00751
00752 case VEH_ROAD:
00753 base_cost = _price.roadveh_base;
00754 expense_type = EXPENSES_ROADVEH_RUN;
00755 break;
00756
00757 case VEH_AIRCRAFT:
00758 base_cost = _price.aircraft_base;
00759 expense_type = EXPENSES_AIRCRAFT_RUN;
00760 break;
00761
00762 case VEH_TRAIN:
00763 base_cost = 2 * ((RailVehInfo(engine_type)->railveh_type == RAILVEH_WAGON) ?
00764 _price.build_railwagon : _price.build_railvehicle);
00765 expense_type = EXPENSES_TRAIN_RUN;
00766 break;
00767
00768 default: NOT_REACHED();
00769 }
00770 return CommandCost(expense_type, (EngInfo(engine_type)->refit_cost * base_cost) >> 10);
00771 }
00772
00773 static void DoDrawVehicle(const Vehicle *v)
00774 {
00775 SpriteID image = v->cur_image;
00776 SpriteID pal = PAL_NONE;
00777
00778 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
00779
00780 AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
00781 v->x_extent, v->y_extent, v->z_extent, v->z_pos, (v->vehstatus & VS_SHADOW) != 0);
00782 }
00783
00784 void ViewportAddVehicles(DrawPixelInfo *dpi)
00785 {
00786
00787 const int l = dpi->left;
00788 const int r = dpi->left + dpi->width;
00789 const int t = dpi->top;
00790 const int b = dpi->top + dpi->height;
00791
00792
00793 int xl, xu, yl, yu;
00794
00795 if (dpi->width + 70 < (1 << (7 + 6))) {
00796 xl = GB(l - 70, 7, 6);
00797 xu = GB(r, 7, 6);
00798 } else {
00799
00800 xl = 0;
00801 xu = 0x3F;
00802 }
00803
00804 if (dpi->height + 70 < (1 << (6 + 6))) {
00805 yl = GB(t - 70, 6, 6) << 6;
00806 yu = GB(b, 6, 6) << 6;
00807 } else {
00808
00809 yl = 0;
00810 yu = 0x3F << 6;
00811 }
00812
00813 for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
00814 for (int x = xl;; x = (x + 1) & 0x3F) {
00815 const Vehicle *v = _vehicle_position_hash[x + y];
00816
00817 while (v != NULL) {
00818 if (!(v->vehstatus & VS_HIDDEN) &&
00819 l <= v->right_coord &&
00820 t <= v->bottom_coord &&
00821 r >= v->left_coord &&
00822 b >= v->top_coord) {
00823 DoDrawVehicle(v);
00824 }
00825 v = v->next_hash;
00826 }
00827
00828 if (x == xu) break;
00829 }
00830
00831 if (y == yu) break;
00832 }
00833 }
00834
00835 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
00836 {
00837 Vehicle *found = NULL, *v;
00838 uint dist, best_dist = UINT_MAX;
00839
00840 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
00841
00842 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
00843 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
00844
00845 FOR_ALL_VEHICLES(v) {
00846 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
00847 x >= v->left_coord && x <= v->right_coord &&
00848 y >= v->top_coord && y <= v->bottom_coord) {
00849
00850 dist = max(
00851 abs(((v->left_coord + v->right_coord) >> 1) - x),
00852 abs(((v->top_coord + v->bottom_coord) >> 1) - y)
00853 );
00854
00855 if (dist < best_dist) {
00856 found = v;
00857 best_dist = dist;
00858 }
00859 }
00860 }
00861
00862 return found;
00863 }
00864
00865 void CheckVehicle32Day(Vehicle *v)
00866 {
00867 if ((v->day_counter & 0x1F) != 0) return;
00868
00869 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
00870 if (callback == CALLBACK_FAILED) return;
00871 if (HasBit(callback, 0)) TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32);
00872 if (HasBit(callback, 1)) v->colourmap = PAL_NONE;
00873 }
00874
00875 void DecreaseVehicleValue(Vehicle *v)
00876 {
00877 v->value -= v->value >> 8;
00878 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
00879 }
00880
00881 static const byte _breakdown_chance[64] = {
00882 3, 3, 3, 3, 3, 3, 3, 3,
00883 4, 4, 5, 5, 6, 6, 7, 7,
00884 8, 8, 9, 9, 10, 10, 11, 11,
00885 12, 13, 13, 13, 13, 14, 15, 16,
00886 17, 19, 21, 25, 28, 31, 34, 37,
00887 40, 44, 48, 52, 56, 60, 64, 68,
00888 72, 80, 90, 100, 110, 120, 130, 140,
00889 150, 170, 190, 210, 230, 250, 250, 250,
00890 };
00891
00892 void CheckVehicleBreakdown(Vehicle *v)
00893 {
00894 int rel, rel_old;
00895
00896
00897 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
00898 if ((rel_old >> 8) != (rel >> 8)) InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
00899
00900 if (v->breakdown_ctr != 0 || v->vehstatus & VS_STOPPED ||
00901 _settings_game.difficulty.vehicle_breakdowns < 1 ||
00902 v->cur_speed < 5 || _game_mode == GM_MENU) {
00903 return;
00904 }
00905
00906 uint32 r = Random();
00907
00908
00909 int chance = v->breakdown_chance + 1;
00910 if (Chance16I(1, 25, r)) chance += 25;
00911 v->breakdown_chance = min(255, chance);
00912
00913
00914 rel = v->reliability;
00915 if (v->type == VEH_SHIP) rel += 0x6666;
00916
00917
00918 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
00919
00920
00921 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
00922 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
00923 v->breakdown_delay = GB(r, 24, 7) + 0x80;
00924 v->breakdown_chance = 0;
00925 }
00926 }
00927
00928 void AgeVehicle(Vehicle *v)
00929 {
00930 if (v->age < 65535) v->age++;
00931
00932 int age = v->age - v->max_age;
00933 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
00934 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
00935 v->reliability_spd_dec <<= 1;
00936 }
00937
00938 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
00939
00940
00941 if (v->Previous() != NULL || v->owner != _local_company) return;
00942
00943
00944 if (GetCompany(v->owner)->engine_renew && GetEngine(v->engine_type)->company_avail != 0) return;
00945
00946 StringID str;
00947 if (age == -DAYS_IN_LEAP_YEAR) {
00948 str = STR_01A0_IS_GETTING_OLD;
00949 } else if (age == 0) {
00950 str = STR_01A1_IS_GETTING_VERY_OLD;
00951 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
00952 str = STR_01A2_IS_GETTING_VERY_OLD_AND;
00953 } else {
00954 return;
00955 }
00956
00957 SetDParam(0, v->index);
00958 AddNewsItem(str, NS_ADVICE, v->index, 0);
00959 }
00960
00968 CommandCost CmdStartStopVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00969 {
00970
00971 if ((flags & DC_AUTOREPLACE) == 0) SetBit(p2, 0);
00972
00973 if (!IsValidVehicleID(p1)) return CMD_ERROR;
00974
00975 Vehicle *v = GetVehicle(p1);
00976
00977 if (!CheckOwnership(v->owner)) return CMD_ERROR;
00978 if (!v->IsPrimaryVehicle()) return CMD_ERROR;
00979
00980 switch (v->type) {
00981 case VEH_TRAIN:
00982 if (v->vehstatus & VS_STOPPED && v->u.rail.cached_power == 0) return_cmd_error(STR_TRAIN_START_NO_CATENARY);
00983 break;
00984
00985 case VEH_SHIP:
00986 case VEH_ROAD:
00987 break;
00988
00989 case VEH_AIRCRAFT:
00990
00991 if (v->u.air.state >= STARTTAKEOFF && v->u.air.state < TERM7) return_cmd_error(STR_A017_AIRCRAFT_IS_IN_FLIGHT);
00992 break;
00993
00994 default: return CMD_ERROR;
00995 }
00996
00997
00998
00999 uint16 callback = GetVehicleCallback(CBID_VEHICLE_START_STOP_CHECK, 0, 0, v->engine_type, v);
01000 if (callback != CALLBACK_FAILED && GB(callback, 0, 8) != 0xFF && HasBit(p2, 0)) {
01001 StringID error = GetGRFStringID(GetEngineGRFID(v->engine_type), 0xD000 + callback);
01002 return_cmd_error(error);
01003 }
01004
01005 if (flags & DC_EXEC) {
01006 static const StringID vehicle_waiting_in_depot[] = {
01007 STR_8814_TRAIN_IS_WAITING_IN_DEPOT,
01008 STR_9016_ROAD_VEHICLE_IS_WAITING,
01009 STR_981C_SHIP_IS_WAITING_IN_DEPOT,
01010 STR_A014_AIRCRAFT_IS_WAITING_IN,
01011 };
01012
01013 static const WindowClass vehicle_list[] = {
01014 WC_TRAINS_LIST,
01015 WC_ROADVEH_LIST,
01016 WC_SHIPS_LIST,
01017 WC_AIRCRAFT_LIST,
01018 };
01019
01020 if (v->IsStoppedInDepot() && (flags & DC_AUTOREPLACE) == 0) DeleteVehicleNews(p1, vehicle_waiting_in_depot[v->type]);
01021
01022 v->vehstatus ^= VS_STOPPED;
01023 if (v->type != VEH_TRAIN) v->cur_speed = 0;
01024 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
01025 InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
01026 InvalidateWindowClasses(vehicle_list[v->type]);
01027 }
01028 return CommandCost();
01029 }
01030
01041 CommandCost CmdMassStartStopVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01042 {
01043 VehicleList list;
01044 CommandCost return_value = CMD_ERROR;
01045 VehicleType vehicle_type = (VehicleType)GB(p2, 0, 5);
01046 bool start_stop = HasBit(p2, 5);
01047 bool vehicle_list_window = HasBit(p2, 6);
01048
01049 if (vehicle_list_window) {
01050 uint32 id = p1;
01051 uint16 window_type = p2 & VLW_MASK;
01052
01053 GenerateVehicleSortList(&list, vehicle_type, _current_company, id, window_type);
01054 } else {
01055
01056 BuildDepotVehicleList(vehicle_type, tile, &list, NULL);
01057 }
01058
01059 for (uint i = 0; i < list.Length(); i++) {
01060 const Vehicle *v = list[i];
01061
01062 if (!!(v->vehstatus & VS_STOPPED) != start_stop) continue;
01063
01064 if (!vehicle_list_window) {
01065 if (vehicle_type == VEH_TRAIN) {
01066 if (CheckTrainInDepot(v, false) == -1) continue;
01067 } else {
01068 if (!(v->vehstatus & VS_HIDDEN)) continue;
01069 }
01070 }
01071
01072 CommandCost ret = DoCommand(tile, v->index, 0, flags, CMD_START_STOP_VEHICLE);
01073
01074 if (CmdSucceeded(ret)) {
01075 return_value = CommandCost();
01076
01077
01078 if (!(flags & DC_EXEC)) break;
01079 }
01080 }
01081
01082 return return_value;
01083 }
01084
01091 CommandCost CmdDepotSellAllVehicles(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01092 {
01093 VehicleList list;
01094
01095 CommandCost cost(EXPENSES_NEW_VEHICLES);
01096 uint sell_command;
01097 VehicleType vehicle_type = (VehicleType)GB(p1, 0, 8);
01098
01099 switch (vehicle_type) {
01100 case VEH_TRAIN: sell_command = CMD_SELL_RAIL_WAGON; break;
01101 case VEH_ROAD: sell_command = CMD_SELL_ROAD_VEH; break;
01102 case VEH_SHIP: sell_command = CMD_SELL_SHIP; break;
01103 case VEH_AIRCRAFT: sell_command = CMD_SELL_AIRCRAFT; break;
01104 default: return CMD_ERROR;
01105 }
01106
01107
01108 BuildDepotVehicleList(vehicle_type, tile, &list, &list);
01109
01110 for (uint i = 0; i < list.Length(); i++) {
01111 CommandCost ret = DoCommand(tile, list[i]->index, 1, flags, sell_command);
01112 if (CmdSucceeded(ret)) cost.AddCost(ret);
01113 }
01114
01115 if (cost.GetCost() == 0) return CMD_ERROR;
01116 return cost;
01117 }
01118
01128 CommandCost CmdDepotMassAutoReplace(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01129 {
01130 VehicleList list;
01131 CommandCost cost = CommandCost(EXPENSES_NEW_VEHICLES);
01132 VehicleType vehicle_type = (VehicleType)GB(p1, 0, 8);
01133 bool all_or_nothing = HasBit(p2, 0);
01134
01135 if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR;
01136
01137
01138 BuildDepotVehicleList(vehicle_type, tile, &list, &list, true);
01139
01140 bool did_something = false;
01141
01142 for (uint i = 0; i < list.Length(); i++) {
01143 Vehicle *v = (Vehicle*)list[i];
01144
01145
01146 if (!v->IsInDepot()) continue;
01147
01148 CommandCost ret = DoCommand(0, v->index, 0, flags, CMD_AUTOREPLACE_VEHICLE);
01149
01150 if (CmdSucceeded(ret)) {
01151 did_something = true;
01152 cost.AddCost(ret);
01153 } else {
01154 if (ret.GetErrorMessage() != STR_AUTOREPLACE_NOTHING_TO_DO && all_or_nothing) {
01155
01156
01157
01158 assert(!(flags & DC_EXEC));
01159
01160 return CMD_ERROR;
01161 }
01162 }
01163 }
01164
01165 if (!did_something) {
01166
01167
01168 cost = CMD_ERROR;
01169 }
01170
01171 return cost;
01172 }
01173
01178 static bool IsUniqueVehicleName(const char *name)
01179 {
01180 const Vehicle *v;
01181
01182 FOR_ALL_VEHICLES(v) {
01183 if (v->name != NULL && strcmp(v->name, name) == 0) return false;
01184 }
01185
01186 return true;
01187 }
01188
01193 static void CloneVehicleName(const Vehicle *src, Vehicle *dst)
01194 {
01195 char buf[256];
01196
01197
01198 size_t number_position;
01199 for (number_position = strlen(src->name); number_position > 0; number_position--) {
01200
01201
01202 if (src->name[number_position - 1] < '0' || src->name[number_position - 1] > '9') break;
01203 }
01204
01205
01206 int num;
01207 if (number_position == strlen(src->name)) {
01208
01209 strecpy(buf, src->name, lastof(buf));
01210 strecat(buf, " ", lastof(buf));
01211 number_position = strlen(buf);
01212 num = 2;
01213 } else {
01214
01215 strecpy(buf, src->name, lastof(buf));
01216 buf[number_position] = '\0';
01217 num = strtol(&src->name[number_position], NULL, 10) + 1;
01218 }
01219
01220
01221 for (int max_iterations = 1000; max_iterations > 0; max_iterations--, num++) {
01222
01223 seprintf(&buf[number_position], lastof(buf), "%d", num);
01224
01225
01226 if (IsUniqueVehicleName(buf)) {
01227 dst->name = strdup(buf);
01228 break;
01229 }
01230 }
01231
01232
01233 }
01234
01241 CommandCost CmdCloneVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01242 {
01243 CommandCost total_cost(EXPENSES_NEW_VEHICLES);
01244 uint32 build_argument = 2;
01245
01246 if (!IsValidVehicleID(p1)) return CMD_ERROR;
01247
01248 Vehicle *v = GetVehicle(p1);
01249 Vehicle *v_front = v;
01250 Vehicle *w = NULL;
01251 Vehicle *w_front = NULL;
01252 Vehicle *w_rear = NULL;
01253
01254
01255
01256
01257
01258
01259
01260
01261
01262 if (!CheckOwnership(v->owner)) return CMD_ERROR;
01263
01264 if (v->type == VEH_TRAIN && (!IsFrontEngine(v) || v->u.rail.crash_anim_pos >= 4400)) return CMD_ERROR;
01265
01266
01267 if (!(flags & DC_EXEC)) {
01268 int veh_counter = 0;
01269 do {
01270 veh_counter++;
01271 } while ((v = v->Next()) != NULL);
01272
01273 if (!Vehicle::AllocateList(NULL, veh_counter)) {
01274 return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME);
01275 }
01276 }
01277
01278 v = v_front;
01279
01280 do {
01281 if (v->type == VEH_TRAIN && IsRearDualheaded(v)) {
01282
01283 continue;
01284 }
01285
01286 CommandCost cost = DoCommand(tile, v->engine_type, build_argument, flags, GetCmdBuildVeh(v));
01287 build_argument = 3;
01288
01289 if (CmdFailed(cost)) return cost;
01290
01291 total_cost.AddCost(cost);
01292
01293 if (flags & DC_EXEC) {
01294 w = GetVehicle(_new_vehicle_id);
01295
01296 if (v->type == VEH_TRAIN && HasBit(v->u.rail.flags, VRF_REVERSE_DIRECTION)) {
01297 SetBit(w->u.rail.flags, VRF_REVERSE_DIRECTION);
01298 }
01299
01300 if (v->type == VEH_TRAIN && !IsFrontEngine(v)) {
01301
01302
01303 CommandCost result = DoCommand(0, (w_rear->index << 16) | w->index, 1, flags, CMD_MOVE_RAIL_VEHICLE);
01304 if (CmdFailed(result)) {
01305
01306
01307 DoCommand(w_front->tile, w_front->index, 1, flags, GetCmdSellVeh(w_front));
01308 DoCommand(w_front->tile, w->index, 1, flags, GetCmdSellVeh(w));
01309 return result;
01310 }
01311 } else {
01312
01313 w_front = w;
01314 w->service_interval = v->service_interval;
01315 }
01316 w_rear = w;
01317 }
01318 } while (v->type == VEH_TRAIN && (v = GetNextVehicle(v)) != NULL);
01319
01320 if (flags & DC_EXEC && v_front->type == VEH_TRAIN) {
01321
01322 _new_vehicle_id = w_front->index;
01323 }
01324
01325 if (flags & DC_EXEC) {
01326
01327 DoCommand(0, v_front->group_id, w_front->index, flags, CMD_ADD_VEHICLE_GROUP);
01328 }
01329
01330
01331
01332 w = w_front;
01333 v = v_front;
01334
01335
01336
01337
01338
01339
01340
01341 do {
01342 do {
01343 if (flags & DC_EXEC) {
01344 assert(w != NULL);
01345
01346 if (w->cargo_type != v->cargo_type || w->cargo_subtype != v->cargo_subtype) {
01347 CommandCost cost = DoCommand(0, w->index, v->cargo_type | (v->cargo_subtype << 8) | 1U << 16 , flags, GetCmdRefitVeh(v));
01348 if (CmdSucceeded(cost)) total_cost.AddCost(cost);
01349 }
01350
01351 if (w->type == VEH_TRAIN && EngineHasArticPart(w)) {
01352 w = GetNextArticPart(w);
01353 } else if (w->type == VEH_ROAD && RoadVehHasArticPart(w)) {
01354 w = w->Next();
01355 } else {
01356 break;
01357 }
01358 } else {
01359 const Engine *e = GetEngine(v->engine_type);
01360 CargoID initial_cargo = (e->CanCarryCargo() ? e->GetDefaultCargoType() : (CargoID)CT_INVALID);
01361
01362 if (v->cargo_type != initial_cargo && initial_cargo != CT_INVALID) {
01363 total_cost.AddCost(GetRefitCost(v->engine_type));
01364 }
01365 }
01366
01367 if (v->type == VEH_TRAIN && EngineHasArticPart(v)) {
01368 v = GetNextArticPart(v);
01369 } else if (v->type == VEH_ROAD && RoadVehHasArticPart(v)) {
01370 v = v->Next();
01371 } else {
01372 break;
01373 }
01374 } while (v != NULL);
01375
01376 if ((flags & DC_EXEC) && v->type == VEH_TRAIN) w = GetNextVehicle(w);
01377 } while (v->type == VEH_TRAIN && (v = GetNextVehicle(v)) != NULL);
01378
01379 if (flags & DC_EXEC) {
01380
01381
01382
01383
01384
01385 DoCommand(0, (v_front->index << 16) | w_front->index, p2 & 1 ? CO_SHARE : CO_COPY, flags, CMD_CLONE_ORDER);
01386
01387
01388 if (v_front->name != NULL) CloneVehicleName(v_front, w_front);
01389 }
01390
01391
01392
01393 if (!CheckCompanyHasMoney(total_cost)) {
01394 if (flags & DC_EXEC) {
01395
01396 DoCommand(w_front->tile, w_front->index, 1, flags, GetCmdSellVeh(w_front));
01397 }
01398 return CMD_ERROR;
01399 }
01400
01401 return total_cost;
01402 }
01403
01413 CommandCost SendAllVehiclesToDepot(VehicleType type, DoCommandFlag flags, bool service, Owner owner, uint16 vlw_flag, uint32 id)
01414 {
01415 VehicleList list;
01416
01417 GenerateVehicleSortList(&list, type, owner, id, vlw_flag);
01418
01419
01420 for (uint i = 0; i < list.Length(); i++) {
01421 const Vehicle *v = list[i];
01422 CommandCost ret = DoCommand(v->tile, v->index, (service ? 1 : 0) | DEPOT_DONT_CANCEL, flags, GetCmdSendToDepot(type));
01423
01424
01425
01426
01427
01428 if (CmdSucceeded(ret) && !(flags & DC_EXEC)) {
01429 return CommandCost();
01430 }
01431 }
01432
01433 return (flags & DC_EXEC) ? CommandCost() : CMD_ERROR;
01434 }
01435
01442 uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *colour)
01443 {
01444 int count = 0;
01445 int max = 0;
01446 int cars = 0;
01447 int unloading = 0;
01448 bool loading = false;
01449
01450 const Vehicle *u = v;
01451 const Station *st = v->last_station_visited != INVALID_STATION ? GetStation(v->last_station_visited) : NULL;
01452
01453
01454 for (; v != NULL; v = v->Next()) {
01455 count += v->cargo.Count();
01456 max += v->cargo_cap;
01457 if (v->cargo_cap != 0 && colour != NULL) {
01458 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
01459 loading |= !(u->current_order.GetUnloadType() & OUFB_UNLOAD) && st->goods[v->cargo_type].days_since_pickup != 255;
01460 cars++;
01461 }
01462 }
01463
01464 if (colour != NULL) {
01465 if (unloading == 0 && loading) {
01466 *colour = STR_PERCENT_UP;
01467 } else if (cars == unloading || !loading) {
01468 *colour = STR_PERCENT_DOWN;
01469 } else {
01470 *colour = STR_PERCENT_UP_DOWN;
01471 }
01472 }
01473
01474
01475 if (max == 0) return 100;
01476
01477
01478 return (count * 100) / max;
01479 }
01480
01481 void VehicleEnterDepot(Vehicle *v)
01482 {
01483 switch (v->type) {
01484 case VEH_TRAIN:
01485 InvalidateWindowClasses(WC_TRAINS_LIST);
01486
01487 SetDepotWaypointReservation(v->tile, false);
01488 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(v->tile);
01489
01490 if (!IsFrontEngine(v)) v = v->First();
01491 UpdateSignalsOnSegment(v->tile, INVALID_DIAGDIR, v->owner);
01492 v->load_unload_time_rem = 0;
01493 ClrBit(v->u.rail.flags, VRF_TOGGLE_REVERSE);
01494 TrainConsistChanged(v, true);
01495 break;
01496
01497 case VEH_ROAD:
01498 InvalidateWindowClasses(WC_ROADVEH_LIST);
01499 if (!IsRoadVehFront(v)) v = v->First();
01500 break;
01501
01502 case VEH_SHIP:
01503 InvalidateWindowClasses(WC_SHIPS_LIST);
01504 v->u.ship.state = TRACK_BIT_DEPOT;
01505 RecalcShipStuff(v);
01506 break;
01507
01508 case VEH_AIRCRAFT:
01509 InvalidateWindowClasses(WC_AIRCRAFT_LIST);
01510 HandleAircraftEnterHangar(v);
01511 break;
01512 default: NOT_REACHED();
01513 }
01514
01515 if (v->type != VEH_TRAIN) {
01516
01517
01518 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01519 }
01520 InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
01521
01522 v->vehstatus |= VS_HIDDEN;
01523 v->cur_speed = 0;
01524
01525 VehicleServiceInDepot(v);
01526
01527 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
01528
01529 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
01530 InvalidateWindow(WC_VEHICLE_VIEW, v->index);
01531
01532 Order t = v->current_order;
01533 v->current_order.MakeDummy();
01534
01535 if (t.IsRefit()) {
01536 _current_company = v->owner;
01537 CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | t.GetRefitSubtype() << 8, DC_EXEC, GetCmdRefitVeh(v));
01538
01539 if (CmdFailed(cost)) {
01540 _vehicles_to_autoreplace[v] = false;
01541 if (v->owner == _local_company) {
01542
01543 SetDParam(0, v->index);
01544 AddNewsItem(STR_ORDER_REFIT_FAILED, NS_ADVICE, v->index, 0);
01545 }
01546 } else if (v->owner == _local_company && cost.GetCost() != 0) {
01547 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
01548 }
01549 }
01550
01551 if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
01552
01553 UpdateVehicleTimetable(v, true);
01554 v->cur_order_index++;
01555 }
01556 if (t.GetDepotActionType() & ODATFB_HALT) {
01557
01558 v->vehstatus |= VS_STOPPED;
01559 if (v->owner == _local_company) {
01560 StringID string;
01561
01562 switch (v->type) {
01563 case VEH_TRAIN: string = STR_8814_TRAIN_IS_WAITING_IN_DEPOT; break;
01564 case VEH_ROAD: string = STR_9016_ROAD_VEHICLE_IS_WAITING; break;
01565 case VEH_SHIP: string = STR_981C_SHIP_IS_WAITING_IN_DEPOT; break;
01566 case VEH_AIRCRAFT: string = STR_A014_AIRCRAFT_IS_WAITING_IN; break;
01567 default: NOT_REACHED(); string = STR_EMPTY;
01568 }
01569
01570 SetDParam(0, v->index);
01571 AddNewsItem(string, NS_ADVICE, v->index, 0);
01572 }
01573 AI::NewEvent(v->owner, new AIEventVehicleWaitingInDepot(v->index));
01574 }
01575 }
01576 }
01577
01584 CommandCost CmdRenameVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01585 {
01586 if (!IsValidVehicleID(p1)) return CMD_ERROR;
01587
01588 Vehicle *v = GetVehicle(p1);
01589 if (!CheckOwnership(v->owner)) return CMD_ERROR;
01590
01591 bool reset = StrEmpty(text);
01592
01593 if (!reset) {
01594 if (strlen(text) >= MAX_LENGTH_VEHICLE_NAME_BYTES) return CMD_ERROR;
01595 if (!(flags & DC_AUTOREPLACE) && !IsUniqueVehicleName(text)) return_cmd_error(STR_NAME_MUST_BE_UNIQUE);
01596 }
01597
01598 if (flags & DC_EXEC) {
01599 free(v->name);
01600 v->name = reset ? NULL : strdup(text);
01601 InvalidateWindowClassesData(WC_TRAINS_LIST, 1);
01602 MarkWholeScreenDirty();
01603 }
01604
01605 return CommandCost();
01606 }
01607
01608
01615 CommandCost CmdChangeServiceInt(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01616 {
01617 uint16 serv_int = GetServiceIntervalClamped(p2);
01618
01619 if (serv_int != p2 || !IsValidVehicleID(p1)) return CMD_ERROR;
01620
01621 Vehicle *v = GetVehicle(p1);
01622
01623 if (!CheckOwnership(v->owner)) return CMD_ERROR;
01624
01625 if (flags & DC_EXEC) {
01626 v->service_interval = serv_int;
01627 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
01628 }
01629
01630 return CommandCost();
01631 }
01632
01633
01634 static Rect _old_vehicle_coords;
01635
01642 void BeginVehicleMove(const Vehicle *v)
01643 {
01644 _old_vehicle_coords.left = v->left_coord;
01645 _old_vehicle_coords.top = v->top_coord;
01646 _old_vehicle_coords.right = v->right_coord;
01647 _old_vehicle_coords.bottom = v->bottom_coord;
01648 }
01649
01656 void EndVehicleMove(const Vehicle *v)
01657 {
01658 MarkAllViewportsDirty(
01659 min(_old_vehicle_coords.left, v->left_coord),
01660 min(_old_vehicle_coords.top, v->top_coord),
01661 max(_old_vehicle_coords.right, v->right_coord) + 1,
01662 max(_old_vehicle_coords.bottom, v->bottom_coord) + 1
01663 );
01664 }
01665
01674 void MarkSingleVehicleDirty(const Vehicle *v)
01675 {
01676 MarkAllViewportsDirty(v->left_coord, v->top_coord, v->right_coord + 1, v->bottom_coord + 1);
01677 }
01678
01683 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
01684 {
01685 static const int8 _delta_coord[16] = {
01686 -1,-1,-1, 0, 1, 1, 1, 0,
01687 -1, 0, 1, 1, 1, 0,-1,-1,
01688 };
01689
01690 int x = v->x_pos + _delta_coord[v->direction];
01691 int y = v->y_pos + _delta_coord[v->direction + 8];
01692
01693 GetNewVehiclePosResult gp;
01694 gp.x = x;
01695 gp.y = y;
01696 gp.old_tile = v->tile;
01697 gp.new_tile = TileVirtXY(x, y);
01698 return gp;
01699 }
01700
01701 static const Direction _new_direction_table[] = {
01702 DIR_N , DIR_NW, DIR_W ,
01703 DIR_NE, DIR_SE, DIR_SW,
01704 DIR_E , DIR_SE, DIR_S
01705 };
01706
01707 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
01708 {
01709 int i = 0;
01710
01711 if (y >= v->y_pos) {
01712 if (y != v->y_pos) i += 3;
01713 i += 3;
01714 }
01715
01716 if (x >= v->x_pos) {
01717 if (x != v->x_pos) i++;
01718 i++;
01719 }
01720
01721 Direction dir = v->direction;
01722
01723 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
01724 if (dirdiff == DIRDIFF_SAME) return dir;
01725 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01726 }
01727
01728 Trackdir GetVehicleTrackdir(const Vehicle *v)
01729 {
01730 if (v->vehstatus & VS_CRASHED) return INVALID_TRACKDIR;
01731
01732 switch (v->type) {
01733 case VEH_TRAIN:
01734 if (v->u.rail.track == TRACK_BIT_DEPOT)
01735 return DiagDirToDiagTrackdir(GetRailDepotDirection(v->tile));
01736
01737 if (v->u.rail.track == TRACK_BIT_WORMHOLE)
01738 return DiagDirToDiagTrackdir(DirToDiagDir(v->direction));
01739
01740 return TrackDirectionToTrackdir(FindFirstTrack(v->u.rail.track), v->direction);
01741
01742 case VEH_SHIP:
01743 if (v->IsInDepot())
01744
01745 return DiagDirToDiagTrackdir(GetShipDepotDirection(v->tile));
01746
01747 if (v->u.ship.state == TRACK_BIT_WORMHOLE)
01748 return DiagDirToDiagTrackdir(DirToDiagDir(v->direction));
01749
01750 return TrackDirectionToTrackdir(FindFirstTrack(v->u.ship.state), v->direction);
01751
01752 case VEH_ROAD:
01753 if (v->IsInDepot())
01754 return DiagDirToDiagTrackdir(GetRoadDepotDirection(v->tile));
01755
01756 if (IsStandardRoadStopTile(v->tile))
01757 return DiagDirToDiagTrackdir(GetRoadStopDir(v->tile));
01758
01759 if (IsDriveThroughStopTile(v->tile)) return DiagDirToDiagTrackdir(DirToDiagDir(v->direction));
01760
01761
01762 if (!IsReversingRoadTrackdir((Trackdir)v->u.road.state)) return (Trackdir)v->u.road.state;
01763
01764
01765 return DiagDirToDiagTrackdir(DirToDiagDir(v->direction));
01766
01767
01768 default: return INVALID_TRACKDIR;
01769 }
01770 }
01771
01781 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
01782 {
01783 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
01784 }
01785
01786 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
01787 {
01788
01789 const Vehicle *v;
01790 FOR_ALL_VEHICLES(v) {
01791 if (v->type == type && v->owner == owner) {
01792 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
01793 }
01794 }
01795
01796 if (this->maxid == 0) return;
01797
01798 this->maxid++;
01799 this->maxid++;
01800
01801 this->cache = CallocT<bool>(this->maxid);
01802
01803
01804 FOR_ALL_VEHICLES(v) {
01805 if (v->type == type && v->owner == owner) {
01806 this->cache[v->unitnumber] = true;
01807 }
01808 }
01809 }
01810
01811 UnitID FreeUnitIDGenerator::NextID()
01812 {
01813 if (this->maxid <= this->curid) return ++this->curid;
01814
01815 while (this->cache[++this->curid]) { }
01816
01817 return this->curid;
01818 }
01819
01820 UnitID GetFreeUnitNumber(VehicleType type)
01821 {
01822 FreeUnitIDGenerator gen(type, _current_company);
01823
01824 return gen.NextID();
01825 }
01826
01827
01836 bool CanBuildVehicleInfrastructure(VehicleType type)
01837 {
01838 assert(IsCompanyBuildableVehicleType(type));
01839
01840 if (!IsValidCompanyID(_local_company)) return false;
01841 if (_settings_client.gui.always_build_infrastructure) return true;
01842
01843 UnitID max;
01844 switch (type) {
01845 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
01846 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
01847 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
01848 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
01849 default: NOT_REACHED();
01850 }
01851
01852
01853 if (max > 0) {
01854
01855 const Engine *e;
01856 FOR_ALL_ENGINES_OF_TYPE(e, type) {
01857 if (HasBit(e->company_avail, _local_company)) return true;
01858 }
01859 return false;
01860 }
01861
01862
01863 const Vehicle *v;
01864 FOR_ALL_VEHICLES(v) {
01865 if (v->owner == _local_company && v->type == type) return true;
01866 }
01867
01868 return false;
01869 }
01870
01871
01872 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01873 {
01874 const Company *c = GetCompany(company);
01875 LiveryScheme scheme = LS_DEFAULT;
01876 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
01877
01878
01879
01880 if (c->livery[LS_DEFAULT].in_use && (_settings_client.gui.liveries == 2 || (_settings_client.gui.liveries == 1 && company == _local_company))) {
01881
01882 const Engine *e = GetEngine(engine_type);
01883 switch (e->type) {
01884 default: NOT_REACHED();
01885 case VEH_TRAIN: {
01886 const RailVehicleInfo *rvi = RailVehInfo(engine_type);
01887 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (IsArticulatedPart(v) && rvi->railveh_type != RAILVEH_WAGON))) {
01888
01889
01890 engine_type = parent_engine_type;
01891 e = GetEngine(engine_type);
01892 rvi = RailVehInfo(engine_type);
01893
01894 }
01895
01896 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01897 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01898 if (rvi->railveh_type == RAILVEH_WAGON) {
01899 if (!GetCargo(cargo_type)->is_freight) {
01900 if (parent_engine_type == INVALID_ENGINE) {
01901 scheme = LS_PASSENGER_WAGON_STEAM;
01902 } else {
01903 switch (RailVehInfo(parent_engine_type)->engclass) {
01904 default: NOT_REACHED();
01905 case EC_STEAM: scheme = LS_PASSENGER_WAGON_STEAM; break;
01906 case EC_DIESEL: scheme = LS_PASSENGER_WAGON_DIESEL; break;
01907 case EC_ELECTRIC: scheme = LS_PASSENGER_WAGON_ELECTRIC; break;
01908 case EC_MONORAIL: scheme = LS_PASSENGER_WAGON_MONORAIL; break;
01909 case EC_MAGLEV: scheme = LS_PASSENGER_WAGON_MAGLEV; break;
01910 }
01911 }
01912 } else {
01913 scheme = LS_FREIGHT_WAGON;
01914 }
01915 } else {
01916 bool is_mu = HasBit(EngInfo(engine_type)->misc_flags, EF_RAIL_IS_MU);
01917
01918 switch (rvi->engclass) {
01919 default: NOT_REACHED();
01920 case EC_STEAM: scheme = LS_STEAM; break;
01921 case EC_DIESEL: scheme = is_mu ? LS_DMU : LS_DIESEL; break;
01922 case EC_ELECTRIC: scheme = is_mu ? LS_EMU : LS_ELECTRIC; break;
01923 case EC_MONORAIL: scheme = LS_MONORAIL; break;
01924 case EC_MAGLEV: scheme = LS_MAGLEV; break;
01925 }
01926 }
01927 break;
01928 }
01929
01930 case VEH_ROAD: {
01931
01932 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
01933 engine_type = parent_engine_type;
01934 e = GetEngine(engine_type);
01935 cargo_type = v->First()->cargo_type;
01936 }
01937 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01938 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01939
01940
01941 if (HasBit(EngInfo(engine_type)->misc_flags, EF_ROAD_TRAM)) {
01942
01943 scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
01944 } else {
01945
01946 scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
01947 }
01948 break;
01949 }
01950
01951 case VEH_SHIP: {
01952 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
01953 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS;
01954 scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
01955 break;
01956 }
01957
01958 case VEH_AIRCRAFT: {
01959 switch (e->u.air.subtype) {
01960 case AIR_HELI: scheme = LS_HELICOPTER; break;
01961 case AIR_CTOL: scheme = LS_SMALL_PLANE; break;
01962 case AIR_CTOL | AIR_FAST: scheme = LS_LARGE_PLANE; break;
01963 }
01964 break;
01965 }
01966 }
01967
01968
01969 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
01970 }
01971
01972 return &c->livery[scheme];
01973 }
01974
01975
01976 static SpriteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
01977 {
01978 SpriteID map = (v != NULL) ? v->colourmap : PAL_NONE;
01979
01980
01981 if (map != PAL_NONE) return map;
01982
01983
01984 if (HasBit(EngInfo(engine_type)->callbackmask, CBM_VEHICLE_COLOUR_REMAP)) {
01985 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
01986
01987
01988 if (callback != CALLBACK_FAILED && callback != 0xC000) {
01989 map = GB(callback, 0, 14);
01990
01991
01992 if (!HasBit(callback, 14)) {
01993
01994 if (v != NULL) ((Vehicle*)v)->colourmap = map;
01995 return map;
01996 }
01997 }
01998 }
01999
02000 bool twocc = HasBit(EngInfo(engine_type)->misc_flags, EF_USES_2CC);
02001
02002 if (map == PAL_NONE) map = twocc ? (SpriteID)SPR_2CCMAP_BASE : (SpriteID)PALETTE_RECOLOUR_START;
02003
02004 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v);
02005
02006 map += livery->colour1;
02007 if (twocc) map += livery->colour2 * 16;
02008
02009
02010 if (v != NULL) ((Vehicle*)v)->colourmap = map;
02011 return map;
02012 }
02013
02014 SpriteID GetEnginePalette(EngineID engine_type, CompanyID company)
02015 {
02016 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
02017 }
02018
02019 SpriteID GetVehiclePalette(const Vehicle *v)
02020 {
02021 if (v->type == VEH_TRAIN) {
02022 return GetEngineColourMap(v->engine_type, v->owner, v->u.rail.first_engine, v);
02023 } else if (v->type == VEH_ROAD) {
02024 return GetEngineColourMap(v->engine_type, v->owner, v->u.road.first_engine, v);
02025 }
02026
02027 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
02028 }
02029
02030
02031 void Vehicle::BeginLoading()
02032 {
02033 assert(IsTileType(tile, MP_STATION) || type == VEH_SHIP);
02034
02035 if (this->current_order.IsType(OT_GOTO_STATION) &&
02036 this->current_order.GetDestination() == this->last_station_visited) {
02037 current_order.MakeLoading(true);
02038 UpdateVehicleTimetable(this, true);
02039
02040
02041
02042
02043
02044
02045 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
02046
02047 } else {
02048 current_order.MakeLoading(false);
02049 }
02050
02051 GetStation(this->last_station_visited)->loading_vehicles.push_back(this);
02052
02053 VehiclePayment(this);
02054
02055 InvalidateWindow(GetWindowClassForVehicleType(this->type), this->owner);
02056 InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
02057 InvalidateWindow(WC_VEHICLE_DETAILS, this->index);
02058 InvalidateWindow(WC_STATION_VIEW, this->last_station_visited);
02059
02060 GetStation(this->last_station_visited)->MarkTilesDirty(true);
02061 this->MarkDirty();
02062 }
02063
02064 void Vehicle::LeaveStation()
02065 {
02066 assert(current_order.IsType(OT_LOADING));
02067
02068
02069 if (current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
02070
02071 current_order.MakeLeaveStation();
02072 Station *st = GetStation(this->last_station_visited);
02073 st->loading_vehicles.remove(this);
02074
02075 HideFillingPercent(&this->fill_percent_te_id);
02076
02077 if (this->type == VEH_TRAIN) {
02078
02079 if (IsTileType(this->tile, MP_STATION)) StationAnimationTrigger(st, this->tile, STAT_ANIM_TRAIN_DEPARTS);
02080
02081
02082
02083
02084 if (UpdateSignalsOnSegment(this->tile, TrackdirToExitdir(GetVehicleTrackdir(this)), this->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) {
02085 TryPathReserve(this, true, true);
02086 }
02087 }
02088 }
02089
02090
02091 void Vehicle::HandleLoading(bool mode)
02092 {
02093 switch (this->current_order.GetType()) {
02094 case OT_LOADING: {
02095 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
02096
02097
02098 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) ||
02099 (_settings_game.order.timetabling && this->current_order_time < wait_time)) return;
02100
02101 this->PlayLeaveStationSound();
02102
02103 bool at_destination_station = this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE;
02104 this->LeaveStation();
02105
02106
02107 if (!at_destination_station) return;
02108 break;
02109 }
02110
02111 case OT_DUMMY: break;
02112
02113 default: return;
02114 }
02115
02116 this->cur_order_index++;
02117 InvalidateVehicleOrder(this, 0);
02118 }
02119
02120 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
02121 {
02122 if (!CheckOwnership(this->owner)) return CMD_ERROR;
02123 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
02124 if (this->IsStoppedInDepot()) return CMD_ERROR;
02125
02126 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
02127 bool halt_in_depot = this->current_order.GetDepotActionType() & ODATFB_HALT;
02128 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
02129
02130
02131
02132 if (flags & DC_EXEC) {
02133 this->current_order.SetDepotOrderType(ODTF_MANUAL);
02134 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
02135 InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
02136 }
02137 return CommandCost();
02138 }
02139
02140 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR;
02141 if (flags & DC_EXEC) {
02142
02143
02144 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->cur_order_index++;
02145
02146 this->current_order.MakeDummy();
02147 InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
02148 }
02149 return CommandCost();
02150 }
02151
02152 TileIndex location;
02153 DestinationID destination;
02154 bool reverse;
02155 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};
02156 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
02157
02158 if (flags & DC_EXEC) {
02159 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
02160
02161 this->dest_tile = location;
02162 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
02163 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
02164 InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
02165
02166
02167 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
02168
02169 if (this->type == VEH_AIRCRAFT && this->u.air.state == FLYING && this->u.air.targetairport != destination) {
02170
02171 extern void AircraftNextAirportPos_and_Order(Vehicle *v);
02172 AircraftNextAirportPos_and_Order(this);
02173 }
02174 }
02175
02176 return CommandCost();
02177
02178 }
02179
02180 void Vehicle::SetNext(Vehicle *next)
02181 {
02182 if (this->next != NULL) {
02183
02184 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02185 v->first = this->next;
02186 }
02187 this->next->previous = NULL;
02188 }
02189
02190 this->next = next;
02191
02192 if (this->next != NULL) {
02193
02194 if (this->next->previous != NULL) this->next->previous->next = NULL;
02195 this->next->previous = this;
02196 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
02197 v->first = this->first;
02198 }
02199 }
02200 }
02201
02202 void Vehicle::AddToShared(Vehicle *shared_chain)
02203 {
02204 assert(this->previous_shared == NULL && this->next_shared == NULL);
02205
02206 if (!shared_chain->orders.list) {
02207 assert(shared_chain->previous_shared == NULL);
02208 assert(shared_chain->next_shared == NULL);
02209 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
02210 }
02211
02212 this->next_shared = shared_chain->next_shared;
02213 this->previous_shared = shared_chain;
02214
02215 shared_chain->next_shared = this;
02216
02217 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
02218
02219 shared_chain->orders.list->AddVehicle(this);
02220 }
02221
02222 void Vehicle::RemoveFromShared()
02223 {
02224
02225
02226 bool were_first = (this->FirstShared() == this);
02227 uint32 old_window_number = (this->FirstShared()->index << 16) | (this->type << 11) | VLW_SHARED_ORDERS | this->owner;
02228
02229 this->orders.list->RemoveVehicle(this);
02230
02231 if (!were_first) {
02232
02233 this->previous_shared->next_shared = this->NextShared();
02234 }
02235
02236 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
02237
02238
02239 if (this->orders.list->GetNumVehicles() == 1) {
02240
02241 DeleteWindowById(GetWindowClassForVehicleType(this->type), old_window_number);
02242 InvalidateVehicleOrder(this->FirstShared(), 0);
02243 } else if (were_first) {
02244
02245
02246 InvalidateWindowData(GetWindowClassForVehicleType(this->type), old_window_number, (this->FirstShared()->index << 16) | (1 << 15));
02247 }
02248
02249 this->next_shared = NULL;
02250 this->previous_shared = NULL;
02251 }
02252
02253 void StopAllVehicles()
02254 {
02255 Vehicle *v;
02256 FOR_ALL_VEHICLES(v) {
02257
02258
02259 v->vehstatus |= VS_STOPPED;
02260 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
02261 InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
02262 }
02263 }
02264
02265 void VehiclesYearlyLoop()
02266 {
02267 Vehicle *v;
02268 FOR_ALL_VEHICLES(v) {
02269 if (v->IsPrimaryVehicle()) {
02270
02271 Money profit = v->GetDisplayProfitThisYear();
02272 if (v->age >= 730 && profit < 0) {
02273 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
02274 SetDParam(0, v->index);
02275 SetDParam(1, profit);
02276 AddNewsItem(
02277 STR_VEHICLE_IS_UNPROFITABLE,
02278 NS_ADVICE,
02279 v->index,
02280 0);
02281 }
02282 AI::NewEvent(v->owner, new AIEventVehicleUnprofitable(v->index));
02283 }
02284
02285 v->profit_last_year = v->profit_this_year;
02286 v->profit_this_year = 0;
02287 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
02288 }
02289 }
02290 }
02291
02292
02302 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
02303 {
02304 assert(IsEngineIndex(engine_type));
02305 const Engine *e = GetEngine(engine_type);
02306
02307 switch (e->type) {
02308 case VEH_TRAIN:
02309 return (st->facilities & FACIL_TRAIN) != 0;
02310
02311 case VEH_ROAD:
02312
02313
02314
02315 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
02316
02317 case VEH_SHIP:
02318 return (st->facilities & FACIL_DOCK) != 0;
02319
02320 case VEH_AIRCRAFT:
02321 return (st->facilities & FACIL_AIRPORT) != 0 &&
02322 (st->Airport()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
02323
02324 default:
02325 return false;
02326 }
02327 }
02328
02335 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
02336 {
02337 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(v) != NULL;
02338
02339 return CanVehicleUseStation(v->engine_type, st);
02340 }