00001
00002
00005 #include "stdafx.h"
00006 #include "openttd.h"
00007 #include "road_map.h"
00008 #include "roadveh.h"
00009 #include "ship.h"
00010 #include "spritecache.h"
00011 #include "tile_cmd.h"
00012 #include "landscape.h"
00013 #include "timetable.h"
00014 #include "viewport_func.h"
00015 #include "gfx_func.h"
00016 #include "news_func.h"
00017 #include "command_func.h"
00018 #include "saveload/saveload.h"
00019 #include "company_func.h"
00020 #include "debug.h"
00021 #include "vehicle_gui.h"
00022 #include "rail_type.h"
00023 #include "train.h"
00024 #include "aircraft.h"
00025 #include "industry_map.h"
00026 #include "station_map.h"
00027 #include "water_map.h"
00028 #include "yapf/yapf.h"
00029 #include "newgrf_callbacks.h"
00030 #include "newgrf_engine.h"
00031 #include "newgrf_sound.h"
00032 #include "newgrf_station.h"
00033 #include "newgrf_text.h"
00034 #include "group.h"
00035 #include "group_gui.h"
00036 #include "order_func.h"
00037 #include "strings_func.h"
00038 #include "zoom_func.h"
00039 #include "functions.h"
00040 #include "date_func.h"
00041 #include "window_func.h"
00042 #include "vehicle_func.h"
00043 #include "signal_func.h"
00044 #include "sound_func.h"
00045 #include "variables.h"
00046 #include "autoreplace_func.h"
00047 #include "autoreplace_gui.h"
00048 #include "string_func.h"
00049 #include "settings_type.h"
00050 #include "oldpool_func.h"
00051 #include "depot_map.h"
00052 #include "animated_tile_func.h"
00053 #include "effectvehicle_base.h"
00054 #include "core/alloc_func.hpp"
00055 #include "core/smallmap_type.hpp"
00056 #include "vehiclelist.h"
00057 #include "core/mem_func.hpp"
00058 #include "depot_func.h"
00059
00060 #include "table/sprites.h"
00061 #include "table/strings.h"
00062
00063 #define GEN_HASH(x, y) ((GB((y), 6, 6) << 6) + GB((x), 7, 6))
00064
00065 VehicleID _vehicle_id_ctr_day;
00066 const Vehicle *_place_clicked_vehicle;
00067 VehicleID _new_vehicle_id;
00068 uint16 _returned_refit_capacity;
00069
00070
00071
00072 const uint32 _veh_build_proc_table[] = {
00073 CMD_BUILD_RAIL_VEHICLE,
00074 CMD_BUILD_ROAD_VEH,
00075 CMD_BUILD_SHIP,
00076 CMD_BUILD_AIRCRAFT,
00077 };
00078 const uint32 _veh_sell_proc_table[] = {
00079 CMD_SELL_RAIL_WAGON,
00080 CMD_SELL_ROAD_VEH,
00081 CMD_SELL_SHIP,
00082 CMD_SELL_AIRCRAFT,
00083 };
00084
00085 const uint32 _veh_refit_proc_table[] = {
00086 CMD_REFIT_RAIL_VEHICLE,
00087 CMD_REFIT_ROAD_VEH,
00088 CMD_REFIT_SHIP,
00089 CMD_REFIT_AIRCRAFT,
00090 };
00091
00092 const uint32 _send_to_depot_proc_table[] = {
00093 CMD_SEND_TRAIN_TO_DEPOT,
00094 CMD_SEND_ROADVEH_TO_DEPOT,
00095 CMD_SEND_SHIP_TO_DEPOT,
00096 CMD_SEND_AIRCRAFT_TO_HANGAR,
00097 };
00098
00099
00100
00101 DEFINE_OLD_POOL_GENERIC(Vehicle, Vehicle)
00102
00103
00107 bool Vehicle::NeedsAutorenewing(const Company *c) const
00108 {
00109
00110
00111
00112
00113 assert(c == GetCompany(this->owner));
00114
00115 if (!c->engine_renew) return false;
00116 if (this->age - this->max_age < (c->engine_renew_months * 30)) return false;
00117 if (this->age == 0) return false;
00118
00119 return true;
00120 }
00121
00122 void VehicleServiceInDepot(Vehicle *v)
00123 {
00124 v->date_of_last_service = _date;
00125 v->breakdowns_since_last_service = 0;
00126 v->reliability = GetEngine(v->engine_type)->reliability;
00127 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
00128 }
00129
00130 bool Vehicle::NeedsServicing() const
00131 {
00132 if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
00133
00134 if (_settings_game.order.no_servicing_if_no_breakdowns && _settings_game.difficulty.vehicle_breakdowns == 0) {
00135
00136
00137 return EngineHasReplacementForCompany(GetCompany(this->owner), this->engine_type, this->group_id);
00138 }
00139
00140 return _settings_game.vehicle.servint_ispercent ?
00141 (this->reliability < GetEngine(this->engine_type)->reliability * (100 - this->service_interval) / 100) :
00142 (this->date_of_last_service + this->service_interval < _date);
00143 }
00144
00145 bool Vehicle::NeedsAutomaticServicing() const
00146 {
00147 if (_settings_game.order.gotodepot && VehicleHasDepotOrders(this)) return false;
00148 if (this->current_order.IsType(OT_LOADING)) return false;
00149 if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
00150 return NeedsServicing();
00151 }
00152
00153 StringID VehicleInTheWayErrMsg(const Vehicle* v)
00154 {
00155 switch (v->type) {
00156 case VEH_TRAIN: return STR_8803_TRAIN_IN_THE_WAY;
00157 case VEH_ROAD: return STR_9000_ROAD_VEHICLE_IN_THE_WAY;
00158 case VEH_AIRCRAFT: return STR_A015_AIRCRAFT_IN_THE_WAY;
00159 default: return STR_980E_SHIP_IN_THE_WAY;
00160 }
00161 }
00162
00163 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
00164 {
00165 byte z = *(byte*)data;
00166
00167 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
00168 if (v->z_pos > z) return NULL;
00169
00170 _error_message = VehicleInTheWayErrMsg(v);
00171 return v;
00172 }
00173
00174 bool EnsureNoVehicleOnGround(TileIndex tile)
00175 {
00176 byte z = GetTileMaxZ(tile);
00177 return !HasVehicleOnPos(tile, &z, &EnsureNoVehicleProcZ);
00178 }
00179
00181 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
00182 {
00183 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
00184 if (v == (const Vehicle *)data) return NULL;
00185
00186 _error_message = VehicleInTheWayErrMsg(v);
00187 return v;
00188 }
00189
00197 bool HasVehicleOnTunnelBridge(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
00198 {
00199 return HasVehicleOnPos(tile, (void *)ignore, &GetVehicleTunnelBridgeProc) ||
00200 HasVehicleOnPos(endtile, (void *)ignore, &GetVehicleTunnelBridgeProc);
00201 }
00202
00203
00204 static void UpdateVehiclePosHash(Vehicle *v, int x, int y);
00205
00206 void VehiclePositionChanged(Vehicle *v)
00207 {
00208 int img = v->cur_image;
00209 Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
00210 const Sprite *spr = GetSprite(img, ST_NORMAL);
00211
00212 pt.x += spr->x_offs;
00213 pt.y += spr->y_offs;
00214
00215 UpdateVehiclePosHash(v, pt.x, pt.y);
00216
00217 v->left_coord = pt.x;
00218 v->top_coord = pt.y;
00219 v->right_coord = pt.x + spr->width + 2;
00220 v->bottom_coord = pt.y + spr->height + 2;
00221 }
00222
00223 Vehicle::Vehicle()
00224 {
00225 this->type = VEH_INVALID;
00226 this->left_coord = INVALID_COORD;
00227 this->group_id = DEFAULT_GROUP;
00228 this->fill_percent_te_id = INVALID_TE_ID;
00229 this->first = this;
00230 this->colormap = PAL_NONE;
00231 }
00232
00237 byte VehicleRandomBits()
00238 {
00239 return GB(Random(), 0, 8);
00240 }
00241
00242
00243 bool Vehicle::AllocateList(Vehicle **vl, int num)
00244 {
00245 if (!Vehicle::CanAllocateItem(num)) return false;
00246 if (vl == NULL) return true;
00247
00248 uint counter = _Vehicle_pool.first_free_index;
00249
00250 for (int i = 0; i != num; i++) {
00251 vl[i] = new (AllocateRaw(counter)) InvalidVehicle();
00252 counter++;
00253 }
00254
00255 return true;
00256 }
00257
00258
00259
00260 const int HASH_BITS = 7;
00261 const int HASH_SIZE = 1 << HASH_BITS;
00262 const int HASH_MASK = HASH_SIZE - 1;
00263 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
00264 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
00265
00266
00267
00268 const int HASH_RES = 0;
00269
00270 static Vehicle *_new_vehicle_position_hash[TOTAL_HASH_SIZE];
00271
00272 static Vehicle *VehicleFromHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
00273 {
00274 for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
00275 for (int x = xl; ; x = (x + 1) & HASH_MASK) {
00276 Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00277 for (; v != NULL; v = v->next_new_hash) {
00278 Vehicle *a = proc(v, data);
00279 if (find_first && a != NULL) return a;
00280 }
00281 if (x == xu) break;
00282 }
00283 if (y == yu) break;
00284 }
00285
00286 return NULL;
00287 }
00288
00289
00301 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
00302 {
00303 const int COLL_DIST = 6;
00304
00305
00306 int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00307 int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00308 int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00309 int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00310
00311 return VehicleFromHash(xl, yl, xu, yu, data, proc, find_first);
00312 }
00313
00328 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00329 {
00330 VehicleFromPosXY(x, y, data, proc, false);
00331 }
00332
00344 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00345 {
00346 return VehicleFromPosXY(x, y, data, proc, true) != NULL;
00347 }
00348
00359 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
00360 {
00361 int x = GB(TileX(tile), HASH_RES, HASH_BITS);
00362 int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
00363
00364 Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00365 for (; v != NULL; v = v->next_new_hash) {
00366 if (v->tile != tile) continue;
00367
00368 Vehicle *a = proc(v, data);
00369 if (find_first && a != NULL) return a;
00370 }
00371
00372 return NULL;
00373 }
00374
00388 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00389 {
00390 VehicleFromPos(tile, data, proc, false);
00391 }
00392
00403 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00404 {
00405 return VehicleFromPos(tile, data, proc, true) != NULL;
00406 }
00407
00408
00409 static void UpdateNewVehiclePosHash(Vehicle *v, bool remove)
00410 {
00411 Vehicle **old_hash = v->old_new_hash;
00412 Vehicle **new_hash;
00413
00414 if (remove) {
00415 new_hash = NULL;
00416 } else {
00417 int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
00418 int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
00419 new_hash = &_new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00420 }
00421
00422 if (old_hash == new_hash) return;
00423
00424
00425 if (old_hash != NULL) {
00426 Vehicle *last = NULL;
00427 Vehicle *u = *old_hash;
00428 while (u != v) {
00429 last = u;
00430 u = u->next_new_hash;
00431 assert(u != NULL);
00432 }
00433
00434 if (last == NULL) {
00435 *old_hash = v->next_new_hash;
00436 } else {
00437 last->next_new_hash = v->next_new_hash;
00438 }
00439 }
00440
00441
00442 if (new_hash != NULL) {
00443 v->next_new_hash = *new_hash;
00444 *new_hash = v;
00445 assert(v != v->next_new_hash);
00446 }
00447
00448
00449 v->old_new_hash = new_hash;
00450 }
00451
00452 static Vehicle *_vehicle_position_hash[0x1000];
00453
00454 static void UpdateVehiclePosHash(Vehicle *v, int x, int y)
00455 {
00456 UpdateNewVehiclePosHash(v, x == INVALID_COORD);
00457
00458 Vehicle **old_hash, **new_hash;
00459 int old_x = v->left_coord;
00460 int old_y = v->top_coord;
00461
00462 new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(x, y)];
00463 old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(old_x, old_y)];
00464
00465 if (old_hash == new_hash) return;
00466
00467
00468 if (old_hash != NULL) {
00469 Vehicle *last = NULL;
00470 Vehicle *u = *old_hash;
00471 while (u != v) {
00472 last = u;
00473 u = u->next_hash;
00474 assert(u != NULL);
00475 }
00476
00477 if (last == NULL) {
00478 *old_hash = v->next_hash;
00479 } else {
00480 last->next_hash = v->next_hash;
00481 }
00482 }
00483
00484
00485 if (new_hash != NULL) {
00486 v->next_hash = *new_hash;
00487 *new_hash = v;
00488 }
00489 }
00490
00491 void ResetVehiclePosHash()
00492 {
00493 Vehicle *v;
00494 FOR_ALL_VEHICLES(v) { v->old_new_hash = NULL; }
00495 memset(_vehicle_position_hash, 0, sizeof(_vehicle_position_hash));
00496 memset(_new_vehicle_position_hash, 0, sizeof(_new_vehicle_position_hash));
00497 }
00498
00499 void ResetVehicleColorMap()
00500 {
00501 Vehicle *v;
00502 FOR_ALL_VEHICLES(v) { v->colormap = PAL_NONE; }
00503 }
00504
00509 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
00510 static AutoreplaceMap _vehicles_to_autoreplace;
00511
00512 void InitializeVehicles()
00513 {
00514 _Vehicle_pool.CleanPool();
00515 _Vehicle_pool.AddBlockToPool();
00516
00517 _vehicles_to_autoreplace.Reset();
00518 ResetVehiclePosHash();
00519 }
00520
00521 Vehicle *GetLastVehicleInChain(Vehicle *v)
00522 {
00523 while (v->Next() != NULL) v = v->Next();
00524 return v;
00525 }
00526
00527 const Vehicle *GetLastVehicleInChain(const Vehicle *v)
00528 {
00529 while (v->Next() != NULL) v = v->Next();
00530 return v;
00531 }
00532
00533 uint CountVehiclesInChain(const Vehicle* v)
00534 {
00535 uint count = 0;
00536 do count++; while ((v = v->Next()) != NULL);
00537 return count;
00538 }
00539
00544 bool IsEngineCountable(const Vehicle *v)
00545 {
00546 switch (v->type) {
00547 case VEH_AIRCRAFT: return IsNormalAircraft(v);
00548 case VEH_TRAIN:
00549 return !IsArticulatedPart(v) &&
00550 !IsRearDualheaded(v);
00551 case VEH_ROAD: return IsRoadVehFront(v);
00552 case VEH_SHIP: return true;
00553 default: return false;
00554 }
00555 }
00556
00557 void Vehicle::PreDestructor()
00558 {
00559 if (CleaningPool()) return;
00560
00561 if (IsValidStationID(this->last_station_visited)) {
00562 GetStation(this->last_station_visited)->loading_vehicles.remove(this);
00563
00564 HideFillingPercent(&this->fill_percent_te_id);
00565 }
00566
00567 if (IsEngineCountable(this)) {
00568 GetCompany(this->owner)->num_engines[this->engine_type]--;
00569 if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
00570
00571 DeleteGroupHighlightOfVehicle(this);
00572 if (IsValidGroupID(this->group_id)) GetGroup(this->group_id)->num_engines[this->engine_type]--;
00573 if (this->IsPrimaryVehicle()) DecreaseGroupNumVehicle(this->group_id);
00574 }
00575
00576 if (this->type == VEH_ROAD) ClearSlot(this);
00577 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
00578 Station *st = GetTargetAirportIfValid(this);
00579 if (st != NULL) {
00580 const AirportFTA *layout = st->Airport()->layout;
00581 CLRBITS(st->airport_flags, layout[this->u.air.previous_pos].block | layout[this->u.air.pos].block);
00582 }
00583 }
00584
00585 if (this->type != VEH_TRAIN || (this->type == VEH_TRAIN && (IsFrontEngine(this) || IsFreeWagon(this)))) {
00586 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
00587 }
00588
00589 if (this->IsPrimaryVehicle()) {
00590 DeleteWindowById(WC_VEHICLE_VIEW, this->index);
00591 DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
00592 DeleteWindowById(WC_VEHICLE_REFIT, this->index);
00593 DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
00594 DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
00595 InvalidateWindow(WC_COMPANY, this->owner);
00596 }
00597 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
00598
00599 this->cargo.Truncate(0);
00600 DeleteVehicleOrders(this);
00601 DeleteDepotHighlightOfVehicle(this);
00602
00603 extern void StopGlobalFollowVehicle(const Vehicle *v);
00604 StopGlobalFollowVehicle(this);
00605 }
00606
00607 Vehicle::~Vehicle()
00608 {
00609 free(this->name);
00610
00611 if (CleaningPool()) return;
00612
00613
00614
00615 if (!(this->vehstatus & VS_HIDDEN)) MarkSingleVehicleDirty(this);
00616
00617 Vehicle *v = this->Next();
00618 this->SetNext(NULL);
00619
00620 delete v;
00621
00622 UpdateVehiclePosHash(this, INVALID_COORD, 0);
00623 this->next_hash = NULL;
00624 this->next_new_hash = NULL;
00625
00626 DeleteVehicleNews(this->index, INVALID_STRING_ID);
00627
00628 new (this) InvalidVehicle();
00629 }
00630
00634 void VehicleEnteredDepotThisTick(Vehicle *v)
00635 {
00636
00637
00638 _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED) &&
00639 (!v->current_order.IsType(OT_GOTO_DEPOT) ||
00640 !(v->current_order.GetDepotActionType() & ODATFB_HALT));
00641
00642
00643
00644
00645
00646
00647 v->vehstatus |= VS_STOPPED;
00648 }
00649
00650 void CallVehicleTicks()
00651 {
00652 _vehicles_to_autoreplace.Clear();
00653
00654 Station *st;
00655 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
00656
00657 Vehicle *v;
00658 FOR_ALL_VEHICLES(v) {
00659 v->Tick();
00660
00661 switch (v->type) {
00662 default: break;
00663
00664 case VEH_TRAIN:
00665 case VEH_ROAD:
00666 case VEH_AIRCRAFT:
00667 case VEH_SHIP:
00668 if (v->type == VEH_TRAIN && IsTrainWagon(v)) continue;
00669 if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
00670 if (v->type == VEH_ROAD && !IsRoadVehFront(v)) continue;
00671
00672 v->motion_counter += (v->direction & 1) ? (v->cur_speed * 3) / 4 : v->cur_speed;
00673
00674 if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
00675
00676
00677 if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
00678 }
00679 }
00680
00681 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
00682 v = it->first;
00683
00684 _current_company = v->owner;
00685
00686
00687
00688
00689 if (it->second) v->vehstatus &= ~VS_STOPPED;
00690
00691
00692 int x = v->x_pos;
00693 int y = v->y_pos;
00694 int z = v->z_pos;
00695
00696 const Company *c = GetCompany(_current_company);
00697 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->engine_renew_money));
00698 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
00699 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->engine_renew_money));
00700
00701 if (!IsLocalCompany()) continue;
00702
00703 if (res.Succeeded()) {
00704 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
00705 continue;
00706 }
00707
00708 StringID error_message = res.GetErrorMessage();
00709 if (error_message == STR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
00710
00711 if (error_message == STR_0003_NOT_ENOUGH_CASH_REQUIRES) error_message = STR_AUTOREPLACE_MONEY_LIMIT;
00712
00713 StringID message;
00714 if (error_message == STR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
00715 message = error_message;
00716 } else {
00717 message = STR_VEHICLE_AUTORENEW_FAILED;
00718 }
00719
00720 SetDParam(0, v->index);
00721 SetDParam(1, error_message);
00722 AddNewsItem(message, NS_ADVICE, v->index, 0);
00723 }
00724
00725 _current_company = OWNER_NONE;
00726 }
00727
00733 bool CanRefitTo(EngineID engine_type, CargoID cid_to)
00734 {
00735 return HasBit(EngInfo(engine_type)->refit_mask, cid_to);
00736 }
00737
00742 CargoID FindFirstRefittableCargo(EngineID engine_type)
00743 {
00744 uint32 refit_mask = EngInfo(engine_type)->refit_mask;
00745
00746 if (refit_mask != 0) {
00747 for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
00748 if (HasBit(refit_mask, cid)) return cid;
00749 }
00750 }
00751
00752 return CT_INVALID;
00753 }
00754
00759 CommandCost GetRefitCost(EngineID engine_type)
00760 {
00761 Money base_cost;
00762 ExpensesType expense_type;
00763 switch (GetEngine(engine_type)->type) {
00764 case VEH_SHIP:
00765 base_cost = _price.ship_base;
00766 expense_type = EXPENSES_SHIP_RUN;
00767 break;
00768
00769 case VEH_ROAD:
00770 base_cost = _price.roadveh_base;
00771 expense_type = EXPENSES_ROADVEH_RUN;
00772 break;
00773
00774 case VEH_AIRCRAFT:
00775 base_cost = _price.aircraft_base;
00776 expense_type = EXPENSES_AIRCRAFT_RUN;
00777 break;
00778
00779 case VEH_TRAIN:
00780 base_cost = 2 * ((RailVehInfo(engine_type)->railveh_type == RAILVEH_WAGON) ?
00781 _price.build_railwagon : _price.build_railvehicle);
00782 expense_type = EXPENSES_TRAIN_RUN;
00783 break;
00784
00785 default: NOT_REACHED();
00786 }
00787 return CommandCost(expense_type, (EngInfo(engine_type)->refit_cost * base_cost) >> 10);
00788 }
00789
00790 static void DoDrawVehicle(const Vehicle *v)
00791 {
00792 SpriteID image = v->cur_image;
00793 SpriteID pal = PAL_NONE;
00794
00795 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
00796
00797 AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
00798 v->x_extent, v->y_extent, v->z_extent, v->z_pos, (v->vehstatus & VS_SHADOW) != 0);
00799 }
00800
00801 void ViewportAddVehicles(DrawPixelInfo *dpi)
00802 {
00803
00804 const int l = dpi->left;
00805 const int r = dpi->left + dpi->width;
00806 const int t = dpi->top;
00807 const int b = dpi->top + dpi->height;
00808
00809
00810 int xl, xu, yl, yu;
00811
00812 if (dpi->width + 70 < (1 << (7 + 6))) {
00813 xl = GB(l - 70, 7, 6);
00814 xu = GB(r, 7, 6);
00815 } else {
00816
00817 xl = 0;
00818 xu = 0x3F;
00819 }
00820
00821 if (dpi->height + 70 < (1 << (6 + 6))) {
00822 yl = GB(t - 70, 6, 6) << 6;
00823 yu = GB(b, 6, 6) << 6;
00824 } else {
00825
00826 yl = 0;
00827 yu = 0x3F << 6;
00828 }
00829
00830 for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
00831 for (int x = xl;; x = (x + 1) & 0x3F) {
00832 const Vehicle *v = _vehicle_position_hash[x + y];
00833
00834 while (v != NULL) {
00835 if (!(v->vehstatus & VS_HIDDEN) &&
00836 l <= v->right_coord &&
00837 t <= v->bottom_coord &&
00838 r >= v->left_coord &&
00839 b >= v->top_coord) {
00840 DoDrawVehicle(v);
00841 }
00842 v = v->next_hash;
00843 }
00844
00845 if (x == xu) break;
00846 }
00847
00848 if (y == yu) break;
00849 }
00850 }
00851
00852 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
00853 {
00854 Vehicle *found = NULL, *v;
00855 uint dist, best_dist = UINT_MAX;
00856
00857 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
00858
00859 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
00860 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
00861
00862 FOR_ALL_VEHICLES(v) {
00863 if ((v->vehstatus & (VS_HIDDEN|VS_UNCLICKABLE)) == 0 &&
00864 x >= v->left_coord && x <= v->right_coord &&
00865 y >= v->top_coord && y <= v->bottom_coord) {
00866
00867 dist = max(
00868 abs(((v->left_coord + v->right_coord) >> 1) - x),
00869 abs(((v->top_coord + v->bottom_coord) >> 1) - y)
00870 );
00871
00872 if (dist < best_dist) {
00873 found = v;
00874 best_dist = dist;
00875 }
00876 }
00877 }
00878
00879 return found;
00880 }
00881
00882 void CheckVehicle32Day(Vehicle *v)
00883 {
00884 if ((v->day_counter & 0x1F) != 0) return;
00885
00886 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
00887 if (callback == CALLBACK_FAILED) return;
00888 if (HasBit(callback, 0)) TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32);
00889 if (HasBit(callback, 1)) v->colormap = PAL_NONE;
00890 }
00891
00892 void DecreaseVehicleValue(Vehicle *v)
00893 {
00894 v->value -= v->value >> 8;
00895 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
00896 }
00897
00898 static const byte _breakdown_chance[64] = {
00899 3, 3, 3, 3, 3, 3, 3, 3,
00900 4, 4, 5, 5, 6, 6, 7, 7,
00901 8, 8, 9, 9, 10, 10, 11, 11,
00902 12, 13, 13, 13, 13, 14, 15, 16,
00903 17, 19, 21, 25, 28, 31, 34, 37,
00904 40, 44, 48, 52, 56, 60, 64, 68,
00905 72, 80, 90, 100, 110, 120, 130, 140,
00906 150, 170, 190, 210, 230, 250, 250, 250,
00907 };
00908
00909 void CheckVehicleBreakdown(Vehicle *v)
00910 {
00911 int rel, rel_old;
00912
00913
00914 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
00915 if ((rel_old >> 8) != (rel >> 8)) InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
00916
00917 if (v->breakdown_ctr != 0 || v->vehstatus & VS_STOPPED ||
00918 _settings_game.difficulty.vehicle_breakdowns < 1 ||
00919 v->cur_speed < 5 || _game_mode == GM_MENU) {
00920 return;
00921 }
00922
00923 uint32 r = Random();
00924
00925
00926 int chance = v->breakdown_chance + 1;
00927 if (Chance16I(1, 25, r)) chance += 25;
00928 v->breakdown_chance = min(255, chance);
00929
00930
00931 rel = v->reliability;
00932 if (v->type == VEH_SHIP) rel += 0x6666;
00933
00934
00935 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
00936
00937
00938 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
00939 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
00940 v->breakdown_delay = GB(r, 24, 7) + 0x80;
00941 v->breakdown_chance = 0;
00942 }
00943 }
00944
00945 static void ShowVehicleGettingOld(Vehicle *v, StringID msg)
00946 {
00947 if (v->owner != _local_company) return;
00948
00949
00950 if (GetCompany(v->owner)->engine_renew && GetEngine(v->engine_type)->company_avail != 0) return;
00951
00952 SetDParam(0, v->index);
00953 AddNewsItem(msg, NS_ADVICE, v->index, 0);
00954 }
00955
00956 void AgeVehicle(Vehicle *v)
00957 {
00958 if (v->age < 65535) v->age++;
00959
00960 int age = v->age - v->max_age;
00961 if (age == 366*0 || age == 366*1 || age == 366*2 || age == 366*3 || age == 366*4) v->reliability_spd_dec <<= 1;
00962
00963 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
00964
00965 if (age == -366) {
00966 ShowVehicleGettingOld(v, STR_01A0_IS_GETTING_OLD);
00967 } else if (age == 0) {
00968 ShowVehicleGettingOld(v, STR_01A1_IS_GETTING_VERY_OLD);
00969 } else if (age > 0 && (age % 366) == 0) {
00970 ShowVehicleGettingOld(v, STR_01A2_IS_GETTING_VERY_OLD_AND);
00971 }
00972 }
00973
00981 CommandCost CmdStartStopVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2, const char *text)
00982 {
00983
00984 if ((flags & DC_AUTOREPLACE) == 0) SetBit(p2, 0);
00985
00986 if (!IsValidVehicleID(p1)) return CMD_ERROR;
00987
00988 Vehicle *v = GetVehicle(p1);
00989
00990 if (!CheckOwnership(v->owner)) return CMD_ERROR;
00991 if (!v->IsPrimaryVehicle()) return CMD_ERROR;
00992
00993 switch (v->type) {
00994 case VEH_TRAIN:
00995 if (v->vehstatus & VS_STOPPED && v->u.rail.cached_power == 0) return_cmd_error(STR_TRAIN_START_NO_CATENARY);
00996 break;
00997
00998 case VEH_SHIP:
00999 case VEH_ROAD:
01000 break;
01001
01002 case VEH_AIRCRAFT:
01003
01004 if (v->u.air.state >= STARTTAKEOFF && v->u.air.state < TERM7) return_cmd_error(STR_A017_AIRCRAFT_IS_IN_FLIGHT);
01005 break;
01006
01007 default: return CMD_ERROR;
01008 }
01009
01010
01011
01012 uint16 callback = GetVehicleCallback(CBID_VEHICLE_START_STOP_CHECK, 0, 0, v->engine_type, v);
01013 if (callback != CALLBACK_FAILED && GB(callback, 0, 8) != 0xFF && HasBit(p2, 0)) {
01014 StringID error = GetGRFStringID(GetEngineGRFID(v->engine_type), 0xD000 + callback);
01015 return_cmd_error(error);
01016 }
01017
01018 if (flags & DC_EXEC) {
01019 static const StringID vehicle_waiting_in_depot[] = {
01020 STR_8814_TRAIN_IS_WAITING_IN_DEPOT,
01021 STR_9016_ROAD_VEHICLE_IS_WAITING,
01022 STR_981C_SHIP_IS_WAITING_IN_DEPOT,
01023 STR_A014_AIRCRAFT_IS_WAITING_IN,
01024 };
01025
01026 static const WindowClass vehicle_list[] = {
01027 WC_TRAINS_LIST,
01028 WC_ROADVEH_LIST,
01029 WC_SHIPS_LIST,
01030 WC_AIRCRAFT_LIST,
01031 };
01032
01033 if (v->IsStoppedInDepot() && (flags & DC_AUTOREPLACE) == 0) DeleteVehicleNews(p1, vehicle_waiting_in_depot[v->type]);
01034
01035 v->vehstatus ^= VS_STOPPED;
01036 if (v->type != VEH_TRAIN) v->cur_speed = 0;
01037 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
01038 InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
01039 InvalidateWindowClasses(vehicle_list[v->type]);
01040 }
01041 return CommandCost();
01042 }
01043
01054 CommandCost CmdMassStartStopVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2, const char *text)
01055 {
01056 VehicleList list;
01057 CommandCost return_value = CMD_ERROR;
01058 VehicleType vehicle_type = (VehicleType)GB(p2, 0, 5);
01059 bool start_stop = HasBit(p2, 5);
01060 bool vehicle_list_window = HasBit(p2, 6);
01061
01062 if (vehicle_list_window) {
01063 uint32 id = p1;
01064 uint16 window_type = p2 & VLW_MASK;
01065
01066 GenerateVehicleSortList(&list, vehicle_type, _current_company, id, window_type);
01067 } else {
01068
01069 BuildDepotVehicleList(vehicle_type, tile, &list, NULL);
01070 }
01071
01072 for (uint i = 0; i < list.Length(); i++) {
01073 const Vehicle *v = list[i];
01074
01075 if (!!(v->vehstatus & VS_STOPPED) != start_stop) continue;
01076
01077 if (!vehicle_list_window) {
01078 if (vehicle_type == VEH_TRAIN) {
01079 if (CheckTrainInDepot(v, false) == -1) continue;
01080 } else {
01081 if (!(v->vehstatus & VS_HIDDEN)) continue;
01082 }
01083 }
01084
01085 CommandCost ret = DoCommand(tile, v->index, 0, flags, CMD_START_STOP_VEHICLE);
01086
01087 if (CmdSucceeded(ret)) {
01088 return_value = CommandCost();
01089
01090
01091 if (!(flags & DC_EXEC)) break;
01092 }
01093 }
01094
01095 return return_value;
01096 }
01097
01104 CommandCost CmdDepotSellAllVehicles(TileIndex tile, uint32 flags, uint32 p1, uint32 p2, const char *text)
01105 {
01106 VehicleList list;
01107
01108 CommandCost cost(EXPENSES_NEW_VEHICLES);
01109 uint sell_command;
01110 VehicleType vehicle_type = (VehicleType)GB(p1, 0, 8);
01111
01112 switch (vehicle_type) {
01113 case VEH_TRAIN: sell_command = CMD_SELL_RAIL_WAGON; break;
01114 case VEH_ROAD: sell_command = CMD_SELL_ROAD_VEH; break;
01115 case VEH_SHIP: sell_command = CMD_SELL_SHIP; break;
01116 case VEH_AIRCRAFT: sell_command = CMD_SELL_AIRCRAFT; break;
01117 default: return CMD_ERROR;
01118 }
01119
01120
01121 BuildDepotVehicleList(vehicle_type, tile, &list, &list);
01122
01123 for (uint i = 0; i < list.Length(); i++) {
01124 CommandCost ret = DoCommand(tile, list[i]->index, 1, flags, sell_command);
01125 if (CmdSucceeded(ret)) cost.AddCost(ret);
01126 }
01127
01128 if (cost.GetCost() == 0) return CMD_ERROR;
01129 return cost;
01130 }
01131
01141 CommandCost CmdDepotMassAutoReplace(TileIndex tile, uint32 flags, uint32 p1, uint32 p2, const char *text)
01142 {
01143 VehicleList list;
01144 CommandCost cost = CommandCost(EXPENSES_NEW_VEHICLES);
01145 VehicleType vehicle_type = (VehicleType)GB(p1, 0, 8);
01146 bool all_or_nothing = HasBit(p2, 0);
01147
01148 if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR;
01149
01150
01151 BuildDepotVehicleList(vehicle_type, tile, &list, &list, true);
01152
01153 bool did_something = false;
01154
01155 for (uint i = 0; i < list.Length(); i++) {
01156 Vehicle *v = (Vehicle*)list[i];
01157
01158
01159 if (!v->IsInDepot()) continue;
01160
01161 CommandCost ret = DoCommand(0, v->index, 0, flags, CMD_AUTOREPLACE_VEHICLE);
01162
01163 if (CmdSucceeded(ret)) {
01164 did_something = true;
01165 cost.AddCost(ret);
01166 } else {
01167 if (ret.GetErrorMessage() != STR_AUTOREPLACE_NOTHING_TO_DO && all_or_nothing) {
01168
01169
01170
01171 assert(!(flags & DC_EXEC));
01172
01173 return CMD_ERROR;
01174 }
01175 }
01176 }
01177
01178 if (!did_something) {
01179
01180
01181 cost = CMD_ERROR;
01182 }
01183
01184 return cost;
01185 }
01186
01193 CommandCost CmdCloneVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2, const char *text)
01194 {
01195 CommandCost total_cost(EXPENSES_NEW_VEHICLES);
01196 uint32 build_argument = 2;
01197
01198 if (!IsValidVehicleID(p1)) return CMD_ERROR;
01199
01200 Vehicle *v = GetVehicle(p1);
01201 Vehicle *v_front = v;
01202 Vehicle *w = NULL;
01203 Vehicle *w_front = NULL;
01204 Vehicle *w_rear = NULL;
01205
01206
01207
01208
01209
01210
01211
01212
01213
01214 if (!CheckOwnership(v->owner)) return CMD_ERROR;
01215
01216 if (v->type == VEH_TRAIN && (!IsFrontEngine(v) || v->u.rail.crash_anim_pos >= 4400)) return CMD_ERROR;
01217
01218
01219 if (!(flags &