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