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.h"
00017 #include "command_func.h"
00018 #include "saveload.h"
00019 #include "player_func.h"
00020 #include "engine.h"
00021 #include "debug.h"
00022 #include "vehicle_gui.h"
00023 #include "depot.h"
00024 #include "station.h"
00025 #include "rail_type.h"
00026 #include "train.h"
00027 #include "aircraft.h"
00028 #include "industry_map.h"
00029 #include "station_map.h"
00030 #include "water_map.h"
00031 #include "network/network.h"
00032 #include "yapf/yapf.h"
00033 #include "newgrf_callbacks.h"
00034 #include "newgrf_engine.h"
00035 #include "newgrf_sound.h"
00036 #include "group.h"
00037 #include "order.h"
00038 #include "strings_func.h"
00039 #include "zoom_func.h"
00040 #include "functions.h"
00041 #include "date_func.h"
00042 #include "window_func.h"
00043 #include "vehicle_func.h"
00044 #include "signal_func.h"
00045 #include "sound_func.h"
00046 #include "variables.h"
00047 #include "autoreplace_func.h"
00048 #include "autoreplace_gui.h"
00049 #include "string_func.h"
00050 #include "settings_type.h"
00051
00052 #include "table/sprites.h"
00053 #include "table/strings.h"
00054
00055 #define INVALID_COORD (0x7fffffff)
00056 #define GEN_HASH(x, y) ((GB((y), 6, 6) << 6) + GB((x), 7, 6))
00057
00058 VehicleID _vehicle_id_ctr_day;
00059 Vehicle *_place_clicked_vehicle;
00060 VehicleID _new_vehicle_id;
00061 uint16 _returned_refit_capacity;
00062
00063
00064
00065 const uint32 _veh_build_proc_table[] = {
00066 CMD_BUILD_RAIL_VEHICLE,
00067 CMD_BUILD_ROAD_VEH,
00068 CMD_BUILD_SHIP,
00069 CMD_BUILD_AIRCRAFT,
00070 };
00071 const uint32 _veh_sell_proc_table[] = {
00072 CMD_SELL_RAIL_WAGON,
00073 CMD_SELL_ROAD_VEH,
00074 CMD_SELL_SHIP,
00075 CMD_SELL_AIRCRAFT,
00076 };
00077
00078 const uint32 _veh_refit_proc_table[] = {
00079 CMD_REFIT_RAIL_VEHICLE,
00080 CMD_REFIT_ROAD_VEH,
00081 CMD_REFIT_SHIP,
00082 CMD_REFIT_AIRCRAFT,
00083 };
00084
00085 const uint32 _send_to_depot_proc_table[] = {
00086 CMD_SEND_TRAIN_TO_DEPOT,
00087 CMD_SEND_ROADVEH_TO_DEPOT,
00088 CMD_SEND_SHIP_TO_DEPOT,
00089 CMD_SEND_AIRCRAFT_TO_HANGAR,
00090 };
00091
00092
00093
00094 DEFINE_OLD_POOL_GENERIC(Vehicle, Vehicle)
00095
00096
00100 bool Vehicle::NeedsAutorenewing(const Player *p) const
00101 {
00102
00103
00104
00105
00106 assert(p == GetPlayer(this->owner));
00107
00108 if (!p->engine_renew) return false;
00109 if (this->age - this->max_age < (p->engine_renew_months * 30)) return false;
00110
00111 return true;
00112 }
00113
00114 void VehicleServiceInDepot(Vehicle *v)
00115 {
00116 v->date_of_last_service = _date;
00117 v->breakdowns_since_last_service = 0;
00118 v->reliability = GetEngine(v->engine_type)->reliability;
00119 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
00120 }
00121
00122 bool Vehicle::NeedsServicing() const
00123 {
00124 if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
00125
00126 if (_patches.no_servicing_if_no_breakdowns && _opt.diff.vehicle_breakdowns == 0) {
00127
00128
00129 return EngineHasReplacementForPlayer(GetPlayer(this->owner), this->engine_type, this->group_id);
00130 }
00131
00132 return _patches.servint_ispercent ?
00133 (this->reliability < GetEngine(this->engine_type)->reliability * (100 - this->service_interval) / 100) :
00134 (this->date_of_last_service + this->service_interval < _date);
00135 }
00136
00137 bool Vehicle::NeedsAutomaticServicing() const
00138 {
00139 if (_patches.gotodepot && VehicleHasDepotOrders(this)) return false;
00140 if (this->current_order.type == OT_LOADING) return false;
00141 if (this->current_order.type == OT_GOTO_DEPOT && this->current_order.flags & OFB_HALT_IN_DEPOT) return false;
00142 return NeedsServicing();
00143 }
00144
00145 StringID VehicleInTheWayErrMsg(const Vehicle* v)
00146 {
00147 switch (v->type) {
00148 case VEH_TRAIN: return STR_8803_TRAIN_IN_THE_WAY;
00149 case VEH_ROAD: return STR_9000_ROAD_VEHICLE_IN_THE_WAY;
00150 case VEH_AIRCRAFT: return STR_A015_AIRCRAFT_IN_THE_WAY;
00151 default: return STR_980E_SHIP_IN_THE_WAY;
00152 }
00153 }
00154
00155 static void *EnsureNoVehicleProcZ(Vehicle *v, void *data)
00156 {
00157 byte z = *(byte*)data;
00158
00159 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
00160 if (v->z_pos > z) return NULL;
00161
00162 _error_message = VehicleInTheWayErrMsg(v);
00163 return v;
00164 }
00165
00166 bool EnsureNoVehicleOnGround(TileIndex tile)
00167 {
00168 byte z = GetTileMaxZ(tile);
00169 return !HasVehicleOnPos(tile, &z, &EnsureNoVehicleProcZ);
00170 }
00171
00173 static void *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
00174 {
00175 if (v->type != VEH_TRAIN && v->type != VEH_ROAD) return NULL;
00176
00177 _error_message = VehicleInTheWayErrMsg(v);
00178 return v;
00179 }
00180
00187 bool HasVehicleOnTunnelBridge(TileIndex tile, TileIndex endtile)
00188 {
00189 return HasVehicleOnPos(tile, NULL, &GetVehicleTunnelBridgeProc) ||
00190 HasVehicleOnPos(endtile, NULL, &GetVehicleTunnelBridgeProc);
00191 }
00192
00193
00194 static void UpdateVehiclePosHash(Vehicle* v, int x, int y);
00195
00196 void VehiclePositionChanged(Vehicle *v)
00197 {
00198 int img = v->cur_image;
00199 Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
00200 const Sprite* spr = GetSprite(img);
00201
00202 pt.x += spr->x_offs;
00203 pt.y += spr->y_offs;
00204
00205 UpdateVehiclePosHash(v, pt.x, pt.y);
00206
00207 v->left_coord = pt.x;
00208 v->top_coord = pt.y;
00209 v->right_coord = pt.x + spr->width + 2;
00210 v->bottom_coord = pt.y + spr->height + 2;
00211 }
00212
00214 void AfterLoadVehicles(bool clear_te_id)
00215 {
00216 Vehicle *v;
00217
00218 FOR_ALL_VEHICLES(v) {
00219
00220 if (v->Next() != NULL) v->Next()->previous = v;
00221
00222 v->UpdateDeltaXY(v->direction);
00223
00224 if (clear_te_id) v->fill_percent_te_id = INVALID_TE_ID;
00225 v->first = NULL;
00226 if (v->type == VEH_TRAIN) v->u.rail.first_engine = INVALID_ENGINE;
00227 if (v->type == VEH_ROAD) v->u.road.first_engine = INVALID_ENGINE;
00228
00229 v->cargo.InvalidateCache();
00230 }
00231
00232 FOR_ALL_VEHICLES(v) {
00233
00234 if (v->Previous() == NULL) {
00235 for (Vehicle *u = v; u != NULL; u = u->Next()) {
00236 u->first = v;
00237 }
00238 }
00239 }
00240
00241 FOR_ALL_VEHICLES(v) {
00242 assert(v->first != NULL);
00243
00244 if (v->type == VEH_TRAIN && (IsFrontEngine(v) || IsFreeWagon(v))) {
00245 if (IsFrontEngine(v)) v->u.rail.last_speed = v->cur_speed;
00246 TrainConsistChanged(v, false);
00247 } else if (v->type == VEH_ROAD && IsRoadVehFront(v)) {
00248 RoadVehUpdateCache(v);
00249 }
00250 }
00251
00252 FOR_ALL_VEHICLES(v) {
00253 switch (v->type) {
00254 case VEH_ROAD:
00255 v->u.road.roadtype = HasBit(EngInfo(v->engine_type)->misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD;
00256 v->u.road.compatible_roadtypes = RoadTypeToRoadTypes(v->u.road.roadtype);
00257
00258 case VEH_TRAIN:
00259 case VEH_SHIP:
00260 v->cur_image = v->GetImage(v->direction);
00261 break;
00262
00263 case VEH_AIRCRAFT:
00264 if (IsNormalAircraft(v)) {
00265 v->cur_image = v->GetImage(v->direction);
00266
00267
00268 Vehicle *shadow = v->Next();
00269 shadow->cur_image = v->cur_image;
00270
00271
00272 if (v->subtype == AIR_HELICOPTER) {
00273 Vehicle *rotor = shadow->Next();
00274 rotor->cur_image = GetRotorImage(v);
00275 }
00276
00277 UpdateAircraftCache(v);
00278 }
00279 break;
00280 default: break;
00281 }
00282
00283 v->left_coord = INVALID_COORD;
00284 VehiclePositionChanged(v);
00285 }
00286 }
00287
00288 Vehicle::Vehicle()
00289 {
00290 this->type = VEH_INVALID;
00291 this->left_coord = INVALID_COORD;
00292 this->group_id = DEFAULT_GROUP;
00293 this->fill_percent_te_id = INVALID_TE_ID;
00294 this->first = this;
00295 this->colormap = PAL_NONE;
00296 }
00297
00302 byte VehicleRandomBits()
00303 {
00304 return GB(Random(), 0, 8);
00305 }
00306
00307
00308 bool Vehicle::AllocateList(Vehicle **vl, int num)
00309 {
00310 uint counter = _Vehicle_pool.first_free_index;
00311
00312 for (int i = 0; i != num; i++) {
00313 Vehicle *v = AllocateRaw(counter);
00314
00315 if (v == NULL) return false;
00316 v = new (v) InvalidVehicle();
00317
00318 if (vl != NULL) {
00319 vl[i] = v;
00320 }
00321 counter++;
00322 }
00323
00324 return true;
00325 }
00326
00327
00328
00329 const int HASH_BITS = 7;
00330 const int HASH_SIZE = 1 << HASH_BITS;
00331 const int HASH_MASK = HASH_SIZE - 1;
00332 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
00333 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
00334
00335
00336
00337 const int HASH_RES = 0;
00338
00339 static Vehicle *_new_vehicle_position_hash[TOTAL_HASH_SIZE];
00340
00341 static void *VehicleFromHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
00342 {
00343 for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
00344 for (int x = xl; ; x = (x + 1) & HASH_MASK) {
00345 Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00346 for (; v != NULL; v = v->next_new_hash) {
00347 void *a = proc(v, data);
00348 if (find_first && a != NULL) return a;
00349 }
00350 if (x == xu) break;
00351 }
00352 if (y == yu) break;
00353 }
00354
00355 return NULL;
00356 }
00357
00358
00370 static void *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
00371 {
00372 const int COLL_DIST = 6;
00373
00374
00375 int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00376 int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
00377 int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00378 int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
00379
00380 return VehicleFromHash(xl, yl, xu, yu, data, proc, find_first);
00381 }
00382
00397 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00398 {
00399 VehicleFromPosXY(x, y, data, proc, false);
00400 }
00401
00413 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
00414 {
00415 return VehicleFromPosXY(x, y, data, proc, true) != NULL;
00416 }
00417
00428 static void *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
00429 {
00430 int x = GB(TileX(tile), HASH_RES, HASH_BITS);
00431 int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
00432
00433 Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00434 for (; v != NULL; v = v->next_new_hash) {
00435 if (v->tile != tile) continue;
00436
00437 void *a = proc(v, data);
00438 if (find_first && a != NULL) return a;
00439 }
00440
00441 return NULL;
00442 }
00443
00457 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00458 {
00459 VehicleFromPos(tile, data, proc, false);
00460 }
00461
00472 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
00473 {
00474 return VehicleFromPos(tile, data, proc, true) != NULL;
00475 }
00476
00477
00478 static void UpdateNewVehiclePosHash(Vehicle *v, bool remove)
00479 {
00480 Vehicle **old_hash = v->old_new_hash;
00481 Vehicle **new_hash;
00482
00483 if (remove) {
00484 new_hash = NULL;
00485 } else {
00486 int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
00487 int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
00488 new_hash = &_new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK];
00489 }
00490
00491 if (old_hash == new_hash) return;
00492
00493
00494 if (old_hash != NULL) {
00495 Vehicle *last = NULL;
00496 Vehicle *u = *old_hash;
00497 while (u != v) {
00498 last = u;
00499 u = u->next_new_hash;
00500 assert(u != NULL);
00501 }
00502
00503 if (last == NULL) {
00504 *old_hash = v->next_new_hash;
00505 } else {
00506 last->next_new_hash = v->next_new_hash;
00507 }
00508 }
00509
00510
00511 if (new_hash != NULL) {
00512 v->next_new_hash = *new_hash;
00513 *new_hash = v;
00514 assert(v != v->next_new_hash);
00515 }
00516
00517
00518 v->old_new_hash = new_hash;
00519 }
00520
00521 static Vehicle *_vehicle_position_hash[0x1000];
00522
00523 static void UpdateVehiclePosHash(Vehicle* v, int x, int y)
00524 {
00525 UpdateNewVehiclePosHash(v, x == INVALID_COORD);
00526
00527 Vehicle **old_hash, **new_hash;
00528 int old_x = v->left_coord;
00529 int old_y = v->top_coord;
00530
00531 new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(x, y)];
00532 old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(old_x, old_y)];
00533
00534 if (old_hash == new_hash) return;
00535
00536
00537 if (old_hash != NULL) {
00538 Vehicle *last = NULL;
00539 Vehicle *u = *old_hash;
00540 while (u != v) {
00541 last = u;
00542 u = u->next_hash;
00543 assert(u != NULL);
00544 }
00545
00546 if (last == NULL) {
00547 *old_hash = v->next_hash;
00548 } else {
00549 last->next_hash = v->next_hash;
00550 }
00551 }
00552
00553
00554 if (new_hash != NULL) {
00555 v->next_hash = *new_hash;
00556 *new_hash = v;
00557 }
00558 }
00559
00560 void ResetVehiclePosHash()
00561 {
00562 Vehicle *v;
00563 FOR_ALL_VEHICLES(v) { v->old_new_hash = NULL; }
00564 memset(_vehicle_position_hash, 0, sizeof(_vehicle_position_hash));
00565 memset(_new_vehicle_position_hash, 0, sizeof(_new_vehicle_position_hash));
00566 }
00567
00568 void ResetVehicleColorMap()
00569 {
00570 Vehicle *v;
00571 FOR_ALL_VEHICLES(v) { v->colormap = PAL_NONE; }
00572 }
00573
00574 void InitializeVehicles()
00575 {
00576 _Vehicle_pool.CleanPool();
00577 _Vehicle_pool.AddBlockToPool();
00578
00579 ResetVehiclePosHash();
00580 }
00581
00582 Vehicle *GetLastVehicleInChain(Vehicle *v)
00583 {
00584 while (v->Next() != NULL) v = v->Next();
00585 return v;
00586 }
00587
00588 uint CountVehiclesInChain(const Vehicle* v)
00589 {
00590 uint count = 0;
00591 do count++; while ((v = v->Next()) != NULL);
00592 return count;
00593 }
00594
00599 bool IsEngineCountable(const Vehicle *v)
00600 {
00601 switch (v->type) {
00602 case VEH_AIRCRAFT: return IsNormalAircraft(v);
00603 case VEH_TRAIN:
00604 return !IsArticulatedPart(v) &&
00605 !IsRearDualheaded(v);
00606 case VEH_ROAD: return IsRoadVehFront(v);
00607 case VEH_SHIP: return true;
00608 default: return false;
00609 }
00610 }
00611
00612 void Vehicle::PreDestructor()
00613 {
00614 if (CleaningPool()) return;
00615
00616 if (IsValidStationID(this->last_station_visited)) {
00617 GetStation(this->last_station_visited)->loading_vehicles.remove(this);
00618
00619 HideFillingPercent(&this->fill_percent_te_id);
00620 }
00621
00622 if (IsEngineCountable(this)) {
00623 GetPlayer(this->owner)->num_engines[this->engine_type]--;
00624 if (this->owner == _local_player) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
00625
00626 if (IsValidGroupID(this->group_id)) GetGroup(this->group_id)->num_engines[this->engine_type]--;
00627 if (this->IsPrimaryVehicle()) DecreaseGroupNumVehicle(this->group_id);
00628 }
00629
00630 if (this->type == VEH_ROAD) ClearSlot(this);
00631 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
00632 Station *st = GetTargetAirportIfValid(this);
00633 if (st != NULL) {
00634 const AirportFTA *layout = st->Airport()->layout;
00635 CLRBITS(st->airport_flags, layout[this->u.air.previous_pos].block | layout[this->u.air.pos].block);
00636 }
00637 }
00638
00639 if (this->type != VEH_TRAIN || (this->type == VEH_TRAIN && (IsFrontEngine(this) || IsFreeWagon(this)))) {
00640 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
00641 }
00642
00643 this->cargo.Truncate(0);
00644 DeleteVehicleOrders(this);
00645
00646
00647
00648
00649 if ((this->type == VEH_TRAIN && EngineHasArticPart(this)) || (this->type == VEH_ROAD && RoadVehHasArticPart(this))) {
00650 delete this->Next();
00651 }
00652
00653 Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
00654 if (w != NULL && WP(w, vp_d).follow_vehicle == this->index) {
00655 ScrollMainWindowTo(this->x_pos, this->y_pos, true);
00656 WP(w, vp_d).follow_vehicle = INVALID_VEHICLE;
00657 }
00658 }
00659
00660 Vehicle::~Vehicle()
00661 {
00662 free(this->name);
00663
00664 if (CleaningPool()) return;
00665
00666 this->SetNext(NULL);
00667 UpdateVehiclePosHash(this, INVALID_COORD, 0);
00668 this->next_hash = NULL;
00669 this->next_new_hash = NULL;
00670
00671 DeleteVehicleNews(this->index, INVALID_STRING_ID);
00672
00673 new (this) InvalidVehicle();
00674 }
00675
00680 void DeleteVehicleChain(Vehicle *v)
00681 {
00682 assert(v->First() == v);
00683
00684 do {
00685 Vehicle *u = v;
00686
00687
00688 if (!(v->vehstatus & VS_HIDDEN)) MarkSingleVehicleDirty(v);
00689 v = v->Next();
00690 delete u;
00691 } while (v != NULL);
00692 }
00693
00695 static Vehicle* _first_veh_in_depot_list;
00696
00700 void VehicleEnteredDepotThisTick(Vehicle *v)
00701 {
00702
00703
00704 if ((HasBit(v->current_order.flags, OF_HALT_IN_DEPOT) && !HasBit(v->current_order.flags, OF_PART_OF_ORDERS) && v->current_order.type == OT_GOTO_DEPOT) ||
00705 (v->vehstatus & VS_STOPPED)) {
00706
00707 v->leave_depot_instantly = false;
00708 } else {
00709
00710
00711
00712 v->vehstatus |= VS_STOPPED;
00713 v->leave_depot_instantly = true;
00714 }
00715
00716 if (_first_veh_in_depot_list == NULL) {
00717 _first_veh_in_depot_list = v;
00718 } else {
00719 Vehicle *w = _first_veh_in_depot_list;
00720 while (w->depot_list != NULL) w = w->depot_list;
00721 w->depot_list = v;
00722 }
00723 }
00724
00725 void CallVehicleTicks()
00726 {
00727 _first_veh_in_depot_list = NULL;
00728
00729 Station *st;
00730 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
00731
00732 Vehicle *v;
00733 FOR_ALL_VEHICLES(v) {
00734 v->Tick();
00735
00736 switch (v->type) {
00737 default: break;
00738
00739 case VEH_TRAIN:
00740 case VEH_ROAD:
00741 case VEH_AIRCRAFT:
00742 case VEH_SHIP:
00743 if (v->type == VEH_TRAIN && IsTrainWagon(v)) continue;
00744 if (v->type == VEH_AIRCRAFT && v->subtype != AIR_HELICOPTER) continue;
00745 if (v->type == VEH_ROAD && !IsRoadVehFront(v)) continue;
00746
00747 v->motion_counter += (v->direction & 1) ? (v->cur_speed * 3) / 4 : v->cur_speed;
00748
00749 if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
00750
00751
00752 if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
00753 }
00754 }
00755
00756
00757 v = _first_veh_in_depot_list;
00758 while (v != NULL) {
00759 Vehicle *w = v->depot_list;
00760 v->depot_list = NULL;
00761 MaybeReplaceVehicle(v, false, true);
00762 v = w;
00763 }
00764 }
00765
00771 bool CanRefitTo(EngineID engine_type, CargoID cid_to)
00772 {
00773 return HasBit(EngInfo(engine_type)->refit_mask, cid_to);
00774 }
00775
00780 CargoID FindFirstRefittableCargo(EngineID engine_type)
00781 {
00782 uint32 refit_mask = EngInfo(engine_type)->refit_mask;
00783
00784 if (refit_mask != 0) {
00785 for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
00786 if (HasBit(refit_mask, cid)) return cid;
00787 }
00788 }
00789
00790 return CT_INVALID;
00791 }
00792
00797 CommandCost GetRefitCost(EngineID engine_type)
00798 {
00799 Money base_cost;
00800 ExpensesType expense_type;
00801 switch (GetEngine(engine_type)->type) {
00802 case VEH_SHIP:
00803 base_cost = _price.ship_base;
00804 expense_type = EXPENSES_SHIP_RUN;
00805 break;
00806
00807 case VEH_ROAD:
00808 base_cost = _price.roadveh_base;
00809 expense_type = EXPENSES_ROADVEH_RUN;
00810 break;
00811
00812 case VEH_AIRCRAFT:
00813 base_cost = _price.aircraft_base;
00814 expense_type = EXPENSES_AIRCRAFT_RUN;
00815 break;
00816
00817 case VEH_TRAIN:
00818 base_cost = 2 * ((RailVehInfo(engine_type)->railveh_type == RAILVEH_WAGON) ?
00819 _price.build_railwagon : _price.build_railvehicle);
00820 expense_type = EXPENSES_TRAIN_RUN;
00821 break;
00822
00823 default: NOT_REACHED();
00824 }
00825 return CommandCost(expense_type, (EngInfo(engine_type)->refit_cost * base_cost) >> 10);
00826 }
00827
00828 static void DoDrawVehicle(const Vehicle *v)
00829 {
00830 SpriteID image = v->cur_image;
00831 SpriteID pal;
00832
00833 if (v->vehstatus & VS_DEFPAL) {
00834 pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
00835 } else {
00836 pal = PAL_NONE;
00837 }
00838
00839 AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
00840 v->sprite_width, v->sprite_height, v->z_height, v->z_pos, (v->vehstatus & VS_SHADOW) != 0);
00841 }
00842
00843 void ViewportAddVehicles(DrawPixelInfo *dpi)
00844 {
00845
00846 const int l = dpi->left;
00847 const int r = dpi->left + dpi->width;
00848 const int t = dpi->top;
00849 const int b = dpi->top + dpi->height;
00850
00851
00852 int xl, xu, yl, yu;
00853
00854 if (dpi->width + 70 < (1 << (7 + 6))) {
00855 xl = GB(l - 70, 7, 6);
00856 xu = GB(r, 7, 6);
00857 } else {
00858
00859 xl = 0;
00860 xu = 0x3F;
00861 }
00862
00863 if (dpi->height + 70 < (1 << (6 + 6))) {
00864 yl = GB(t - 70, 6, 6) << 6;
00865 yu = GB(b, 6, 6) << 6;
00866 } else {
00867
00868 yl = 0;
00869 yu = 0x3F << 6;
00870 }
00871
00872 for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
00873 for (int x = xl;; x = (x + 1) & 0x3F) {
00874 const Vehicle *v = _vehicle_position_hash[x + y];
00875
00876 while (v != NULL) {
00877 if (!(v->vehstatus & VS_HIDDEN) &&
00878 l <= v->right_coord &&
00879 t <= v->bottom_coord &&
00880 r >= v->left_coord &&
00881 b >= v->top_coord) {
00882 DoDrawVehicle(v);
00883 }
00884 v = v->next_hash;
00885 }
00886
00887 if (x == xu) break;
00888 }
00889
00890 if (y == yu) break;
00891 }
00892 }
00893
00894 static void ChimneySmokeInit(Vehicle *v)
00895 {
00896 uint32 r = Random();
00897 v->cur_image = SPR_CHIMNEY_SMOKE_0 + GB(r, 0, 3);
00898 v->progress = GB(r, 16, 3);
00899 }
00900
00901 static void ChimneySmokeTick(Vehicle *v)
00902 {
00903 if (v->progress > 0) {
00904 v->progress--;
00905 } else {
00906 TileIndex tile;
00907
00908 BeginVehicleMove(v);
00909
00910 tile = TileVirtXY(v->x_pos, v->y_pos);
00911 if (!IsTileType(tile, MP_INDUSTRY)) {
00912 EndVehicleMove(v);
00913 delete v;
00914 return;
00915 }
00916
00917 if (v->cur_image != SPR_CHIMNEY_SMOKE_7) {
00918 v->cur_image++;
00919 } else {
00920 v->cur_image = SPR_CHIMNEY_SMOKE_0;
00921 }
00922 v->progress = 7;
00923 VehiclePositionChanged(v);
00924 EndVehicleMove(v);
00925 }
00926 }
00927
00928 static void SteamSmokeInit(Vehicle *v)
00929 {
00930 v->cur_image = SPR_STEAM_SMOKE_0;
00931 v->progress = 12;
00932 }
00933
00934 static void SteamSmokeTick(Vehicle *v)
00935 {
00936 bool moved = false;
00937
00938 BeginVehicleMove(v);
00939
00940 v->progress++;
00941
00942 if ((v->progress & 7) == 0) {
00943 v->z_pos++;
00944 moved = true;
00945 }
00946
00947 if ((v->progress & 0xF) == 4) {
00948 if (v->cur_image != SPR_STEAM_SMOKE_4) {
00949 v->cur_image++;
00950 } else {
00951 EndVehicleMove(v);
00952 delete v;
00953 return;
00954 }
00955 moved = true;
00956 }
00957
00958 if (moved) {
00959 VehiclePositionChanged(v);
00960 EndVehicleMove(v);
00961 }
00962 }
00963
00964 static void DieselSmokeInit(Vehicle *v)
00965 {
00966 v->cur_image = SPR_DIESEL_SMOKE_0;
00967 v->progress = 0;
00968 }
00969
00970 static void DieselSmokeTick(Vehicle *v)
00971 {
00972 v->progress++;
00973
00974 if ((v->progress & 3) == 0) {
00975 BeginVehicleMove(v);
00976 v->z_pos++;
00977 VehiclePositionChanged(v);
00978 EndVehicleMove(v);
00979 } else if ((v->progress & 7) == 1) {
00980 BeginVehicleMove(v);
00981 if (v->cur_image != SPR_DIESEL_SMOKE_5) {
00982 v->cur_image++;
00983 VehiclePositionChanged(v);
00984 EndVehicleMove(v);
00985 } else {
00986 EndVehicleMove(v);
00987 delete v;
00988 }
00989 }
00990 }
00991
00992 static void ElectricSparkInit(Vehicle *v)
00993 {
00994 v->cur_image = SPR_ELECTRIC_SPARK_0;
00995 v->progress = 1;
00996 }
00997
00998 static void ElectricSparkTick(Vehicle *v)
00999 {
01000 if (v->progress < 2) {
01001 v->progress++;
01002 } else {
01003 v->progress = 0;
01004 BeginVehicleMove(v);
01005 if (v->cur_image != SPR_ELECTRIC_SPARK_5) {
01006 v->cur_image++;
01007 VehiclePositionChanged(v);
01008 EndVehicleMove(v);
01009 } else {
01010 EndVehicleMove(v);
01011 delete v;
01012 }
01013 }
01014 }
01015
01016 static void SmokeInit(Vehicle *v)
01017 {
01018 v->cur_image = SPR_SMOKE_0;
01019 v->progress = 12;
01020 }
01021
01022 static void SmokeTick(Vehicle *v)
01023 {
01024 bool moved = false;
01025
01026 BeginVehicleMove(v);
01027
01028 v->progress++;
01029
01030 if ((v->progress & 3) == 0) {
01031 v->z_pos++;
01032 moved = true;
01033 }
01034
01035 if ((v->progress & 0xF) == 4) {
01036 if (v->cur_image != SPR_SMOKE_4) {
01037 v->cur_image++;
01038 } else {
01039 EndVehicleMove(v);
01040 delete v;
01041 return;
01042 }
01043 moved = true;
01044 }
01045
01046 if (moved) {
01047 VehiclePositionChanged(v);
01048 EndVehicleMove(v);
01049 }
01050 }
01051
01052 static void ExplosionLargeInit(Vehicle *v)
01053 {
01054 v->cur_image = SPR_EXPLOSION_LARGE_0;
01055 v->progress = 0;
01056 }
01057
01058 static void ExplosionLargeTick(Vehicle *v)
01059 {
01060 v->progress++;
01061 if ((v->progress & 3) == 0) {
01062 BeginVehicleMove(v);
01063 if (v->cur_image != SPR_EXPLOSION_LARGE_F) {
01064 v->cur_image++;
01065 VehiclePositionChanged(v);
01066 EndVehicleMove(v);
01067 } else {
01068 EndVehicleMove(v);
01069 delete v;
01070 }
01071 }
01072 }
01073
01074 static void BreakdownSmokeInit(Vehicle *v)
01075 {
01076 v->cur_image = SPR_BREAKDOWN_SMOKE_0;
01077 v->progress = 0;
01078 }
01079
01080 static void BreakdownSmokeTick(Vehicle *v)
01081 {
01082 v->progress++;
01083 if ((v->progress & 7) == 0) {
01084 BeginVehicleMove(v);
01085 if (v->cur_image != SPR_BREAKDOWN_SMOKE_3) {
01086 v->cur_image++;
01087 } else {
01088 v->cur_image = SPR_BREAKDOWN_SMOKE_0;
01089 }
01090 VehiclePositionChanged(v);
01091 EndVehicleMove(v);
01092 }
01093
01094 v->u.special.animation_state--;
01095 if (v->u.special.animation_state == 0) {
01096 BeginVehicleMove(v);
01097 EndVehicleMove(v);
01098 delete v;
01099 }
01100 }
01101
01102 static void ExplosionSmallInit(Vehicle *v)
01103 {
01104 v->cur_image = SPR_EXPLOSION_SMALL_0;
01105 v->progress = 0;
01106 }
01107
01108 static void ExplosionSmallTick(Vehicle *v)
01109 {
01110 v->progress++;
01111 if ((v->progress & 3) == 0) {
01112 BeginVehicleMove(v);
01113 if (v->cur_image != SPR_EXPLOSION_SMALL_B) {
01114 v->cur_image++;
01115 VehiclePositionChanged(v);
01116 EndVehicleMove(v);
01117 } else {
01118 EndVehicleMove(v);
01119 delete v;
01120 }
01121 }
01122 }
01123
01124 static void BulldozerInit(Vehicle *v)
01125 {
01126 v->cur_image = SPR_BULLDOZER_NE;
01127 v->progress = 0;
01128 v->u.special.animation_state = 0;
01129 v->u.special.animation_substate = 0;
01130 }
01131
01132 struct BulldozerMovement {
01133 byte direction:2;
01134 byte image:2;
01135 byte duration:3;
01136 };
01137
01138 static const BulldozerMovement _bulldozer_movement[] = {
01139 { 0, 0, 4 },
01140 { 3, 3, 4 },
01141 { 2, 2, 7 },
01142 { 0, 2, 7 },
01143 { 1, 1, 3 },
01144 { 2, 2, 7 },
01145 { 0, 2, 7 },
01146 { 1, 1, 3 },
01147 { 2, 2, 7 },
01148 { 0, 2, 7 },
01149 { 3, 3, 6 },
01150 { 2, 2, 6 },
01151 { 1, 1, 7 },
01152 { 3, 1, 7 },
01153 { 0, 0, 3 },
01154 { 1, 1, 7 },
01155 { 3, 1, 7 },
01156 { 0, 0, 3 },
01157 { 1, 1, 7 },
01158 { 3, 1, 7 }
01159 };
01160
01161 static const struct {
01162 int8 x;
01163 int8 y;
01164 } _inc_by_dir[] = {
01165 { -1, 0 },
01166 { 0, 1 },
01167 { 1, 0 },
01168 { 0, -1 }
01169 };
01170
01171 static void BulldozerTick(Vehicle *v)
01172 {
01173 v->progress++;
01174 if ((v->progress & 7) == 0) {
01175 const BulldozerMovement* b = &_bulldozer_movement[v->u.special.animation_state];
01176
01177 BeginVehicleMove(v);
01178
01179 v->cur_image = SPR_BULLDOZER_NE + b->image;
01180
01181 v->x_pos += _inc_by_dir[b->direction].x;
01182 v->y_pos += _inc_by_dir[b->direction].y;
01183
01184 v->u.special.animation_substate++;
01185 if (v->u.special.animation_substate >= b->duration) {
01186 v->u.special.animation_substate = 0;
01187 v->u.special.animation_state++;
01188 if (v->u.special.animation_state == lengthof(_bulldozer_movement)) {
01189 EndVehicleMove(v);
01190 delete v;
01191 return;
01192 }
01193 }
01194 VehiclePositionChanged(v);
01195 EndVehicleMove(v);
01196 }
01197 }
01198
01199 static void BubbleInit(Vehicle *v)
01200 {
01201 v->cur_image = SPR_BUBBLE_GENERATE_0;
01202 v->spritenum = 0;
01203 v->progress = 0;
01204 }
01205
01206 struct BubbleMovement {
01207 int8 x:4;
01208 int8 y:4;
01209 int8 z:4;
01210 byte image:4;
01211 };
01212
01213 #define MK(x, y, z, i) { x, y, z, i }
01214 #define ME(i) { i, 4, 0, 0 }
01215
01216 static const BubbleMovement _bubble_float_sw[] = {
01217 MK(0, 0, 1, 0),
01218 MK(1, 0, 1, 1),
01219 MK(0, 0, 1, 0),
01220 MK(1, 0, 1, 2),
01221 ME(1)
01222 };
01223
01224
01225 static const BubbleMovement _bubble_float_ne[] = {
01226 MK( 0, 0, 1, 0),
01227 MK(-1, 0, 1, 1),
01228 MK( 0, 0, 1, 0),
01229 MK(-1, 0, 1, 2),
01230 ME(1)
01231 };
01232
01233 static const BubbleMovement _bubble_float_se[] = {
01234 MK(0, 0, 1, 0),
01235 MK(0, 1, 1, 1),
01236 MK(0, 0, 1, 0),
01237 MK(0, 1, 1, 2),
01238 ME(1)
01239 };
01240
01241 static const BubbleMovement _bubble_float_nw[] = {
01242 MK(0, 0, 1, 0),
01243 MK(0, -1, 1, 1),
01244 MK(0, 0, 1, 0),
01245 MK(0, -1, 1, 2),
01246 ME(1)
01247 };
01248
01249 static const BubbleMovement _bubble_burst[] = {
01250 MK(0, 0, 1, 2),
01251 MK(0, 0, 1, 7),
01252 MK(0, 0, 1, 8),
01253 MK(0, 0, 1, 9),
01254 ME(0)
01255 };
01256
01257 static const BubbleMovement _bubble_absorb[] = {
01258 MK(0, 0, 1, 0),
01259 MK(0, 0, 1, 1),
01260 MK(0, 0, 1, 0),
01261 MK(0, 0, 1, 2),
01262 MK(0, 0, 1, 0),
01263 MK(0, 0, 1, 1),
01264 MK(0, 0, 1, 0),
01265 MK(0, 0, 1, 2),
01266 MK(0, 0, 1, 0),
01267 MK(0, 0, 1, 1),
01268 MK(0, 0, 1, 0),
01269 MK(0, 0, 1, 2),
01270 MK(0, 0, 1, 0),
01271 MK(0, 0, 1, 1),
01272 MK(0, 0, 1, 0),
01273 MK(0, 0, 1, 2),
01274 MK(0, 0, 1, 0),
01275 MK(0, 0, 1, 1),
01276 MK(0, 0, 1, 0),
01277 MK(0, 0, 1, 2),
01278 MK(0, 0, 1, 0),
01279 MK(0, 0, 1, 1),
01280 MK(0, 0, 1, 0),
01281 MK(0, 0, 1, 2),
01282 MK(0, 0, 1, 0),
01283 MK(0, 0, 1, 1),
01284 MK(0, 0, 1, 0),
01285 MK(0, 0, 1, 2),
01286 MK(0, 0, 1, 0),
01287 MK(0, 0, 1, 1),
01288 MK(0, 0, 1, 0),
01289 MK(0, 0, 1, 2),
01290 MK(0, 0, 1, 0),
01291 MK(0, 0, 1, 1),
01292 MK(0, 0, 1, 0),
01293 MK(0, 0, 1, 2),
01294 MK(0, 0, 1, 0),
01295 MK(0, 0, 1, 1),
01296 MK(0, 0, 1, 0),
01297 MK(0, 0, 1, 2),
01298 MK(0, 0, 1, 0),
01299 MK(0, 0, 1, 1),
01300 MK(0, 0, 1, 0),
01301 MK(0, 0, 1, 2),
01302 MK(0, 0, 1, 0),
01303 MK(0, 0, 1, 1),
01304 MK(0, 0, 1, 0),
01305 MK(0, 0, 1, 2),
01306 MK(0, 0, 1, 0),
01307 MK(0, 0, 1, 1),
01308 MK(0, 0, 1, 0),
01309 MK(0, 0, 1, 2),
01310 MK(0, 0, 1, 0),
01311 MK(0, 0, 1, 1),
01312 MK(0, 0, 1, 0),
01313 MK(0, 0, 1, 2),
01314 MK(0, 0, 1, 0),
01315 MK(0, 0, 1, 1),
01316 MK(0, 0, 1, 0),
01317 MK(0, 0, 1, 2),
01318 MK(0, 0, 1, 0),
01319 MK(0, 0, 1, 1),
01320 MK(2, 1, 3, 0),
01321 MK(1, 1, 3, 1),
01322 MK(2, 1, 3, 0),
01323 MK(1, 1, 3, 2),
01324 MK(2, 1, 3, 0),
01325 MK(1, 1, 3, 1),
01326 MK(2, 1, 3, 0),
01327 MK(1, 0, 1, 2),
01328 MK(0, 0, 1, 0),
01329 MK(1, 0, 1, 1),
01330 MK(0, 0, 1, 0),
01331 MK(1, 0, 1, 2),
01332 MK(0, 0, 1, 0),
01333 MK(1, 0, 1, 1),
01334 MK(0, 0, 1, 0),
01335 MK(1, 0, 1, 2),
01336 ME(2),
01337 MK(0, 0, 0, 0xA),
01338 MK(0, 0, 0, 0xB),
01339 MK(0, 0, 0, 0xC),
01340 MK(0, 0, 0, 0xD),
01341 MK(0, 0, 0, 0xE),
01342 ME(0)
01343 };
01344 #undef ME
01345 #undef MK
01346
01347 static const BubbleMovement * const _bubble_movement[] = {
01348 _bubble_float_sw,
01349 _bubble_float_ne,
01350 _bubble_float_se,
01351 _bubble_float_nw,
01352 _bubble_burst,
01353 _bubble_absorb,
01354 };
01355
01356 static void BubbleTick(Vehicle *v)
01357 {
01358 uint et;
01359 const BubbleMovement *b;
01360
01361 v->progress++;
01362 if ((v->progress & 3) != 0)
01363 return;
01364
01365 BeginVehicleMove(v);
01366
01367 if (v->spritenum == 0) {
01368 v->cur_image++;
01369 if (v->cur_image < SPR_BUBBLE_GENERATE_3) {
01370 VehiclePositionChanged(v);
01371 EndVehicleMove(v);
01372 return;
01373 }
01374 if (v->u.special.animation_substate != 0) {
01375 v->spritenum = GB(Random(), 0, 2) + 1;
01376 } else {
01377 v->spritenum = 6;
01378 }
01379 et = 0;
01380 } else {
01381 et = v->engine_type + 1;
01382 }
01383
01384 b = &_bubble_movement[v->spritenum - 1][et];
01385
01386 if (b->y == 4 && b->x == 0) {
01387 EndVehicleMove(v);
01388 delete v;
01389 return;
01390 }
01391
01392 if (b->y == 4 && b->x == 1) {
01393 if (v->z_pos > 180 || Chance16I(1, 96, Random())) {
01394 v->spritenum = 5;
01395 SndPlayVehicleFx(SND_2F_POP, v);
01396 }
01397 et = 0;
01398 }
01399
01400 if (b->y == 4 && b->x == 2) {
01401 TileIndex tile;
01402
01403 et++;
01404 SndPlayVehicleFx(SND_31_EXTRACT, v);
01405
01406 tile = TileVirtXY(v->x_pos, v->y_pos);
01407 if (IsTileType(tile, MP_INDUSTRY) && GetIndustryGfx(tile) == GFX_BUBBLE_CATCHER) AddAnimatedTile(tile);
01408 }
01409
01410 v->engine_type = et;
01411 b = &_bubble_movement[v->spritenum - 1][et];
01412
01413 v->x_pos += b->x;
01414 v->y_pos += b->y;
01415 v->z_pos += b->z;
01416 v->cur_image = SPR_BUBBLE_0 + b->image;
01417
01418 VehiclePositionChanged(v);
01419 EndVehicleMove(v);
01420 }
01421
01422
01423 typedef void EffectInitProc(Vehicle *v);
01424 typedef void EffectTickProc(Vehicle *v);
01425
01426 static EffectInitProc * const _effect_init_procs[] = {
01427 ChimneySmokeInit,
01428 SteamSmokeInit,
01429 DieselSmokeInit,
01430 ElectricSparkInit,
01431 SmokeInit,
01432 ExplosionLargeInit,
01433 BreakdownSmokeInit,
01434 ExplosionSmallInit,
01435 BulldozerInit,
01436 BubbleInit,
01437 };
01438
01439 static EffectTickProc * const _effect_tick_procs[] = {
01440 ChimneySmokeTick,
01441 SteamSmokeTick,
01442 DieselSmokeTick,
01443 ElectricSparkTick,
01444 SmokeTick,
01445 ExplosionLargeTick,
01446 BreakdownSmokeTick,
01447 ExplosionSmallTick,
01448 BulldozerTick,
01449 BubbleTick,
01450 };
01451
01452
01453 Vehicle *CreateEffectVehicle(int x, int y, int z, EffectVehicle type)
01454 {
01455 Vehicle *v;
01456
01457 v = new SpecialVehicle();
01458 if (v != NULL) {
01459 v->subtype = type;
01460 v->x_pos = x;
01461 v->y_pos = y;
01462 v->z_pos = z;
01463 v->tile = 0;
01464 v->UpdateDeltaXY(INVALID_DIR);
01465 v->vehstatus = VS_UNCLICKABLE;
01466
01467 _effect_init_procs[type](v);
01468
01469 VehiclePositionChanged(v);
01470 BeginVehicleMove(v);
01471 EndVehicleMove(v);
01472 }
01473 return v;
01474 }
01475
01476 Vehicle *CreateEffectVehicleAbove(int x, int y, int z, EffectVehicle type)
01477 {
01478 int safe_x = Clamp(x, 0, MapMaxX() * TILE_SIZE);
01479 int safe_y = Clamp(y, 0, MapMaxY() * TILE_SIZE);
01480 return CreateEffectVehicle(x, y, GetSlopeZ(safe_x, safe_y) + z, type);
01481 }
01482
01483 Vehicle *CreateEffectVehicleRel(const Vehicle *v, int x, int y, int z, EffectVehicle type)
01484 {
01485 return CreateEffectVehicle(v->x_pos + x, v->y_pos + y, v->z_pos + z, type);
01486 }
01487
01488 void SpecialVehicle::Tick()
01489 {
01490 _effect_tick_procs[this->subtype](this);
01491 }
01492
01493 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
01494 {
01495 Vehicle *found = NULL, *v;
01496 uint dist, best_dist = (uint)-1;
01497
01498 if ( (uint)(x -= vp->left) >= (uint)vp->width ||
01499 (uint)(y -= vp->top) >= (uint)vp->height)
01500 return NULL;
01501
01502 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
01503 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
01504
01505 FOR_ALL_VEHICLES(v) {
01506 if ((v->vehstatus & (VS_HIDDEN|VS_UNCLICKABLE)) == 0 &&
01507 x >= v->left_coord && x <= v->right_coord &&
01508 y >= v->top_coord && y <= v->bottom_coord) {
01509
01510 dist = max(
01511 abs( ((v->left_coord + v->right_coord)>>1) - x ),
01512 abs( ((v->top_coord + v->bottom_coord)>>1) - y )
01513 );
01514
01515 if (dist < best_dist) {
01516 found = v;
01517 best_dist = dist;
01518 }
01519 }
01520 }
01521
01522 return found;
01523 }
01524
01525 void CheckVehicle32Day(Vehicle *v)
01526 {
01527 if ((v->day_counter & 0x1F) != 0) return;
01528
01529 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
01530 if (callback == CALLBACK_FAILED) return;
01531 if (HasBit(callback, 0)) TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32);
01532 if (HasBit(callback, 1)) v->colormap = PAL_NONE;
01533 }
01534
01535 void DecreaseVehicleValue(Vehicle *v)
01536 {
01537 v->value -= v->value >> 8;
01538 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
01539 }
01540
01541 static const byte _breakdown_chance[64] = {
01542 3, 3, 3, 3, 3, 3, 3, 3,
01543 4, 4, 5, 5, 6, 6, 7, 7,
01544 8, 8, 9, 9, 10, 10, 11, 11,
01545 12, 13, 13, 13, 13, 14, 15, 16,
01546 17, 19, 21, 25, 28, 31, 34, 37,
01547 40, 44, 48, 52, 56, 60, 64, 68,
01548 72, 80, 90, 100, 110, 120, 130, 140,
01549 150, 170, 190, 210, 230, 250, 250, 250,
01550 };
01551
01552 void CheckVehicleBreakdown(Vehicle *v)
01553 {
01554 int rel, rel_old;
01555 uint32 r;
01556 int chance;
01557
01558
01559 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
01560 if ((rel_old >> 8) != (rel >> 8))
01561 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
01562
01563 if (v->breakdown_ctr != 0 || v->vehstatus & VS_STOPPED ||
01564 v->cur_speed < 5 || _game_mode == GM_MENU) {
01565 return;
01566 }
01567
01568 r = Random();
01569
01570
01571 chance = v->breakdown_chance + 1;
01572 if (Chance16I(1,25,r)) chance += 25;
01573 v->breakdown_chance = min(255, chance);
01574
01575
01576 rel = v->reliability;
01577 if (v->type == VEH_SHIP) rel += 0x6666;
01578
01579
01580 if (_opt.diff.vehicle_breakdowns < 1) return;
01581
01582
01583 if (_opt.diff.vehicle_breakdowns == 1) rel += 0x6666;
01584
01585
01586 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
01587 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
01588 v->breakdown_delay = GB(r, 24, 7) + 0x80;
01589 v->breakdown_chance = 0;
01590 }
01591 }
01592
01593 static const StringID _vehicle_type_names[4] = {
01594 STR_019F_TRAIN,
01595 STR_019C_ROAD_VEHICLE,
01596 STR_019E_SHIP,
01597 STR_019D_AIRCRAFT,
01598 };
01599
01600 static void ShowVehicleGettingOld(Vehicle *v, StringID msg)
01601 {
01602 if (v->owner != _local_player) return;
01603
01604
01605 if (GetPlayer(v->owner)->engine_renew && GetEngine(v->engine_type)->player_avail != 0) return;
01606
01607 SetDParam(0, _vehicle_type_names[v->type]);
01608 SetDParam(1, v->unitnumber);
01609 AddNewsItem(msg, NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, 0), v->index, 0);
01610 }
01611
01612 void AgeVehicle(Vehicle *v)
01613 {
01614 if (v->age < 65535) v->age++;
01615
01616 int age = v->age - v->max_age;
01617 if (age == 366*0 || age == 366*1 || age == 366*2 || age == 366*3 || age == 366*4) v->reliability_spd_dec <<= 1;
01618
01619 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
01620
01621 if (age == -366) {
01622 ShowVehicleGettingOld(v, STR_01A0_IS_GETTING_OLD);
01623 } else if (age == 0) {
01624 ShowVehicleGettingOld(v, STR_01A1_IS_GETTING_VERY_OLD);
01625 } else if (age > 0 && (age % 366) == 0) {
01626 ShowVehicleGettingOld(v, STR_01A2_IS_GETTING_VERY_OLD_AND);
01627 }
01628 }
01629
01640 CommandCost CmdMassStartStopVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
01641 {
01642 Vehicle **vl = NULL;
01643 uint16 engine_list_length = 0;
01644 uint16 engine_count = 0;
01645 CommandCost return_value = CMD_ERROR;
01646 uint i;
01647 uint stop_command;
01648 VehicleType vehicle_type = (VehicleType)GB(p2, 0, 5);
01649 bool start_stop = HasBit(p2, 5);
01650 bool vehicle_list_window = HasBit(p2, 6);
01651
01652 switch (vehicle_type) {
01653 case VEH_TRAIN: stop_command = CMD_START_STOP_TRAIN; break;
01654 case VEH_ROAD: stop_command = CMD_START_STOP_ROADVEH; break;
01655 case VEH_SHIP: stop_command = CMD_START_STOP_SHIP; break;
01656 case VEH_AIRCRAFT: stop_command = CMD_START_STOP_AIRCRAFT; break;
01657 default: return CMD_ERROR;
01658 }
01659
01660 if (vehicle_list_window) {
01661 uint32 id = p1;
01662 uint16 window_type = p2 & VLW_MASK;
01663
01664 engine_count = GenerateVehicleSortList((const Vehicle***)&vl, &engine_list_length, vehicle_type, _current_player, id, window_type);
01665 } else {
01666
01667 BuildDepotVehicleList(vehicle_type, tile, &vl, &engine_list_length, &engine_count, NULL, NULL, NULL);
01668 }
01669
01670 for (i = 0; i < engine_count; i++) {
01671 const Vehicle *v = vl[i];
01672 CommandCost ret;
01673
01674 if (!!(v->vehstatus & VS_STOPPED) != start_stop) continue;
01675
01676 if (!vehicle_list_window) {
01677 if (vehicle_type == VEH_TRAIN) {
01678 if (CheckTrainInDepot(v, false) == -1) continue;
01679 } else {
01680 if (!(v->vehstatus & VS_HIDDEN)) continue;
01681 }
01682 }
01683
01684 ret = DoCommand(tile, v->index, 0, flags, stop_command);
01685
01686 if (CmdSucceeded(ret)) {
01687 return_value = CommandCost();
01688
01689
01690 if (!(flags & DC_EXEC)) break;
01691 }
01692 }
01693
01694 free(vl);
01695 return return_value;
01696 }
01697
01704 CommandCost CmdDepotSellAllVehicles(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
01705 {
01706 Vehicle **engines = NULL;
01707 Vehicle **wagons = NULL;
01708 uint16 engine_list_length = 0;
01709 uint16 engine_count = 0;
01710 uint16 wagon_list_length = 0;
01711 uint16 wagon_count = 0;
01712
01713 CommandCost cost(EXPENSES_NEW_VEHICLES);
01714 uint i, sell_command, total_number_vehicles;
01715 VehicleType vehicle_type = (VehicleType)GB(p1, 0, 8);
01716
01717 switch (vehicle_type) {
01718 case VEH_TRAIN: sell_command = CMD_SELL_RAIL_WAGON; break;
01719 case VEH_ROAD: sell_command = CMD_SELL_ROAD_VEH; break;
01720 case VEH_SHIP: sell_command = CMD_SELL_SHIP; break;
01721 case VEH_AIRCRAFT: sell_command = CMD_SELL_AIRCRAFT; break;
01722 default: return CMD_ERROR;
01723 }
01724
01725
01726 BuildDepotVehicleList(vehicle_type, tile, &engines, &engine_list_length, &engine_count,
01727 &wagons, &wagon_list_length, &wagon_count);
01728
01729 total_number_vehicles = engine_count + wagon_count;
01730 for (i = 0; i < total_number_vehicles; i++) {
01731 const Vehicle *v;
01732 CommandCost ret;
01733
01734 if (i < engine_count) {
01735 v = engines[i];
01736 } else {
01737 v = wagons[i - engine_count];
01738 }
01739
01740 ret = DoCommand(tile, v->index, 1, flags, sell_command);
01741
01742 if (CmdSucceeded(ret)) cost.AddCost(ret);
01743 }
01744
01745 free(engines);
01746 free(wagons);
01747 if (cost.GetCost() == 0) return CMD_ERROR;
01748 return cost;
01749 }
01750
01757 CommandCost CmdDepotMassAutoReplace(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
01758 {
01759 Vehicle **vl = NULL;
01760 uint16 engine_list_length = 0;
01761 uint16 engine_count = 0;
01762 uint i, x = 0, y = 0, z = 0;
01763 CommandCost cost;
01764 VehicleType vehicle_type = (VehicleType)GB(p1, 0, 8);
01765
01766 if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_player)) return CMD_ERROR;
01767
01768
01769 BuildDepotVehicleList(vehicle_type, tile, &vl, &engine_list_length, &engine_count, NULL, NULL, NULL);
01770
01771
01772 for (i = 0; i < engine_count; i++) {
01773 Vehicle *v = vl[i];
01774 bool stopped = !(v->vehstatus & VS_STOPPED);
01775 CommandCost ret;
01776
01777
01778 if (!v->IsInDepot()) continue;
01779
01780 x = v->x_pos;
01781 y = v->y_pos;
01782 z = v->z_pos;
01783
01784 if (stopped) {
01785 v->vehstatus |= VS_STOPPED;
01786 v->leave_depot_instantly = true;
01787 }
01788 ret = MaybeReplaceVehicle(v, !(flags & DC_EXEC), false);
01789
01790 if (CmdSucceeded(ret)) {
01791 cost.AddCost(ret);
01792 if (!(flags & DC_EXEC)) break;
01793
01794
01795
01796
01797
01798 SubtractMoneyFromPlayer(ret);
01799 }
01800 }
01801
01802 if (cost.GetCost() == 0) {
01803 cost = CMD_ERROR;
01804 } else {
01805 if (flags & DC_EXEC) {
01806
01807 if (IsLocalPlayer()) ShowCostOrIncomeAnimation(x, y, z, cost.GetCost());
01808 }
01809 cost = CommandCost();
01810 }
01811
01812 free(vl);
01813 return cost;
01814 }
01815
01822 CommandCost CmdCloneVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
01823 {
01824 Vehicle *v_front, *v;
01825 Vehicle *w_front, *w, *w_rear;
01826 CommandCost cost, total_cost(EXPENSES_NEW_VEHICLES);
01827 uint32 build_argument = 2;
01828
01829 if (!IsValidVehicleID(p1)) return CMD_ERROR;
01830 v = GetVehicle(p1);
01831 v_front = v;
01832 w = NULL;
01833 w_front = NULL;
01834 w_rear = NULL;
01835
01836
01837
01838
01839
01840
01841
01842
01843
01844
01845 if (!CheckOwnership(v->owner)) return CMD_ERROR;
01846
01847 if (v->type == VEH_TRAIN && (!IsFrontEngine(v) || v->u.rail.crash_anim_pos >= 4400)) return CMD_ERROR;
01848
01849
01850 if (!(flags & DC_EXEC)) {
01851 int veh_counter = 0;
01852 do {
01853 veh_counter++;
01854 } while ((v = v->Next()) != NULL);
01855
01856 if (!Vehicle::AllocateList(NULL, veh_counter)) {
01857 return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME);
01858 }
01859 }
01860
01861 v = v_front;
01862
01863 do {
01864 if (v->type == VEH_TRAIN && IsRearDualheaded(v)) {
01865
01866 continue;
01867 }
01868
01869 cost = DoCommand(tile, v->engine_type, build_argument, flags, GetCmdBuildVeh(v));
01870 build_argument = 3;
01871
01872 if (CmdFailed(cost)) return cost;
01873
01874 total_cost.AddCost(cost);
01875
01876 if (flags & DC_EXEC) {
01877 w = GetVehicle(_new_vehicle_id);
01878
01879 if (v->type == VEH_TRAIN && HasBit(v->u.rail.flags, VRF_REVERSE_DIRECTION)) {
01880 SetBit(w->u.rail.flags, VRF_REVERSE_DIRECTION);
01881 }
01882
01883 if (v->type == VEH_TRAIN && !IsFrontEngine(v)) {
01884
01885
01886 CommandCost result = DoCommand(0, (w_rear->index << 16) | w->index, 1, flags, CMD_MOVE_RAIL_VEHICLE);
01887 if (CmdFailed(result)) {
01888
01889
01890 DoCommand(w_front->tile, w_front->index, 1, flags, GetCmdSellVeh(w_front));
01891 DoCommand(w_front->tile, w->index, 1, flags, GetCmdSellVeh(w));
01892 return result;
01893 }
01894 } else {
01895
01896 w_front = w;
01897 w->service_interval = v->service_interval;
01898 }
01899 w_rear = w;
01900 }
01901 } while (v->type == VEH_TRAIN && (v = GetNextVehicle(v)) != NULL);
01902
01903 if (flags & DC_EXEC && v_front->type == VEH_TRAIN) {
01904
01905 _new_vehicle_id = w_front->index;
01906 }
01907
01908 if (flags & DC_EXEC) {
01909
01910 DoCommand(0, v_front->group_id, w_front->index, flags, CMD_ADD_VEHICLE_GROUP);
01911 }
01912
01913
01914
01915 w = w_front;
01916 v = v_front;
01917
01918
01919
01920
01921
01922
01923
01924 do {
01925 do {
01926 if (flags & DC_EXEC) {
01927 assert(w != NULL);
01928
01929 if (w->cargo_type != v->cargo_type || w->cargo_subtype != v->cargo_subtype) {
01930 cost = DoCommand(0, w->index, v->cargo_type | (v->cargo_subtype << 8) | 1U << 16 , flags, GetCmdRefitVeh(v));
01931 if (CmdSucceeded(cost)) total_cost.AddCost(cost);
01932 }
01933
01934 if (w->type == VEH_TRAIN && EngineHasArticPart(w)) {
01935 w = GetNextArticPart(w);
01936 } else if (w->type == VEH_ROAD && RoadVehHasArticPart(w)) {
01937 w = w->Next();
01938 } else {
01939 break;
01940 }
01941 } else {
01942 CargoID initial_cargo = GetEngineCargoType(v->engine_type);
01943
01944 if (v->cargo_type != initial_cargo && initial_cargo != CT_INVALID) {
01945 total_cost.AddCost(GetRefitCost(v->engine_type));
01946 }
01947 }
01948
01949 if (v->type == VEH_TRAIN && EngineHasArticPart(v)) {
01950 v = GetNextArticPart(v);
01951 } else if (v->type == VEH_ROAD && RoadVehHasArticPart(v)) {
01952 v = v->Next();
01953 } else {
01954 break;
01955 }
01956 } while (v != NULL);
01957
01958 if ((flags & DC_EXEC) && v->type == VEH_TRAIN) w = GetNextVehicle(w);
01959 } while (v->type == VEH_TRAIN && (v = GetNextVehicle(v)) != NULL);
01960
01961 if (flags & DC_EXEC) {
01962
01963
01964
01965
01966
01967 DoCommand(0, (v_front->index << 16) | w_front->index, p2 & 1 ? CO_SHARE : CO_COPY, flags, CMD_CLONE_ORDER);
01968 }
01969
01970
01971
01972 if (!CheckPlayerHasMoney(total_cost)) {
01973 if (flags & DC_EXEC) {
01974
01975 DoCommand(w_front->tile, w_front->index, 1, flags, GetCmdSellVeh(w_front));
01976 }
01977 return CMD_ERROR;
01978 }
01979
01980 return total_cost;
01981 }
01982
01983
01984
01985 static inline void ExtendVehicleListSize(const Vehicle ***engine_list, uint16 *engine_list_length, uint16 step_size)
01986 {
01987 *engine_list_length = min(*engine_list_length + step_size, GetMaxVehicleIndex() + 1);
01988 *engine_list = ReallocT(*engine_list, *engine_list_length);
01989 }
01990
02003 void BuildDepotVehicleList(VehicleType type, TileIndex tile, Vehicle ***engine_list, uint16 *engine_list_length, uint16 *engine_count, Vehicle ***wagon_list, uint16 *wagon_list_length, uint16 *wagon_count)
02004 {
02005 Vehicle *v;
02006
02007
02008 assert(!(engine_list == NULL && type != VEH_TRAIN));
02009 assert(!(type == VEH_TRAIN && engine_list == NULL && wagon_list == NULL));
02010
02011
02012 assert((engine_list == NULL && engine_list_length == NULL) || (engine_list != NULL && engine_list_length != NULL));
02013 assert((wagon_list == NULL && wagon_list_length == NULL) || (wagon_list != NULL && wagon_list_length != NULL));
02014
02015 assert(!(engine_list != NULL && engine_count == NULL));
02016 assert(!(wagon_list != NULL && wagon_count == NULL));
02017
02018 if (engine_count != NULL) *engine_count = 0;
02019 if (wagon_count != NULL) *wagon_count = 0;
02020
02021 switch (type) {
02022 case VEH_TRAIN:
02023 FOR_ALL_VEHICLES(v) {
02024 if (v->tile == tile && v->type == VEH_TRAIN && v->u.rail.track == TRACK_BIT_DEPOT) {
02025 if (IsFrontEngine(v)) {
02026 if (engine_list == NULL) continue;
02027 if (*engine_count == *engine_list_length) ExtendVehicleListSize((const Vehicle***)engine_list, engine_list_length, 25);
02028 (*engine_list)[(*engine_count)++] = v;
02029 } else if (IsFreeWagon(v)) {
02030 if (wagon_list == NULL) continue;
02031 if (*wagon_count == *wagon_list_length) ExtendVehicleListSize((const Vehicle***)wagon_list, wagon_list_length, 25);
02032 (*wagon_list)[(*wagon_count)++] = v;
02033 }
02034 }
02035 }
02036 break;
02037
02038 case VEH_ROAD:
02039 FOR_ALL_VEHICLES(v) {
02040 if (v->tile == tile && v->type == VEH_ROAD && v->IsInDepot() && IsRoadVehFront(v)) {
02041 if (*engine_count == *engine_list_length) ExtendVehicleListSize((const Vehicle***)engine_list, engine_list_length, 25);
02042 (*engine_list)[(*engine_count)++] = v;
02043 }
02044 }
02045 break;
02046
02047 case VEH_SHIP:
02048 FOR_ALL_VEHICLES(v) {
02049 if (v->tile == tile && v->type == VEH_SHIP && v->IsInDepot()) {
02050 if (*engine_count == *engine_list_length) ExtendVehicleListSize((const Vehicle***)engine_list, engine_list_length, 25);
02051 (*engine_list)[(*engine_count)++] = v;
02052 }
02053 }
02054 break;
02055
02056 case VEH_AIRCRAFT:
02057 FOR_ALL_VEHICLES(v) {
02058 if (v->tile == tile &&
02059 v->type == VEH_AIRCRAFT && IsNormalAircraft(v) &&
02060 v->IsInDepot()) {
02061 if (*engine_count == *engine_list_length) ExtendVehicleListSize((const Vehicle***)engine_list, engine_list_length, 25);
02062 (*engine_list)[(*engine_count)++] = v;
02063 }
02064 }
02065 break;
02066
02067 default: NOT_REACHED();
02068 }
02069 }
02070
02087 uint GenerateVehicleSortList(const Vehicle ***sort_list, uint16 *length_of_array, VehicleType type, PlayerID owner, uint32 index, uint16 window_type)
02088 {
02089 uint n = 0;
02090 const Vehicle *v;
02091
02092 switch (window_type) {
02093 case VLW_STATION_LIST: {
02094 FOR_ALL_VEHICLES(v) {
02095 if (v->type == type && v->IsPrimaryVehicle()) {
02096 const Order *order;
02097
02098 FOR_VEHICLE_ORDERS(v, order) {
02099 if (order->type == OT_GOTO_STATION && order->dest == index) {
02100 if (n == *length_of_array) ExtendVehicleListSize(sort_list, length_of_array, 50);
02101 (*sort_list)[n++] = v;
02102 break;
02103 }
02104 }
02105 }
02106 }
02107 break;
02108 }
02109
02110 case VLW_SHARED_ORDERS: {
02111 FOR_ALL_VEHICLES(v) {
02112
02113 if (v->orders != NULL && v->orders->index == index) break;
02114 }
02115
02116 if (v != NULL && v->orders != NULL && v->orders->index == index) {
02117
02118 for (v = GetFirstVehicleFromSharedList(v); v != NULL; v = v->next_shared) {
02119 if (n == *length_of_array) ExtendVehicleListSize(sort_list, length_of_array, 25);
02120 (*sort_list)[n++] = v;
02121 }
02122 }
02123 break;
02124 }
02125
02126 case VLW_STANDARD: {
02127 FOR_ALL_VEHICLES(v) {
02128 if (v->type == type && v->owner == owner && v->IsPrimaryVehicle()) {
02129
02130 if (n == *length_of_array) ExtendVehicleListSize(sort_list, length_of_array, GetNumVehicles()/4);
02131 (*sort_list)[n++] = v;
02132 }
02133 }
02134 break;
02135 }
02136
02137 case VLW_DEPOT_LIST: {
02138 FOR_ALL_VEHICLES(v) {
02139 if (v->type == type && v->IsPrimaryVehicle()) {
02140 const Order *order;
02141
02142 FOR_VEHICLE_ORDERS(v, order) {
02143 if (order->type == OT_GOTO_DEPOT && order->dest == index) {
02144 if (n == *length_of_array) ExtendVehicleListSize(sort_list, length_of_array, 25);
02145 (*sort_list)[n++] = v;
02146 break;
02147 }
02148 }
02149 }
02150 }
02151 break;
02152 }
02153
02154 case VLW_GROUP_LIST:
02155 FOR_ALL_VEHICLES(v) {
02156 if (v->type == type && v->IsPrimaryVehicle() &&
02157 v->owner == owner && v->group_id == index) {
02158 if (n == *length_of_array) ExtendVehicleListSize(sort_list, length_of_array, GetNumVehicles() / 4);
02159
02160 (*sort_list)[n++] = v;
02161 }
02162 }
02163 break;
02164
02165 default: NOT_REACHED(); break;
02166 }
02167
02168 if ((n + 100) < *length_of_array) {
02169
02170
02171
02172
02173 *length_of_array = n + 50;
02174 *sort_list = ReallocT(*sort_list, (*length_of_array) * sizeof((*sort_list)[0]));
02175 }
02176
02177 return n;
02178 }
02179
02188 CommandCost SendAllVehiclesToDepot(VehicleType type, uint32 flags, bool service, PlayerID owner, uint16 vlw_flag, uint32 id)
02189 {
02190 const Vehicle **sort_list = NULL;
02191 uint n, i;
02192 uint16 array_length = 0;
02193
02194 n = GenerateVehicleSortList(&sort_list, &array_length, type, owner, id, vlw_flag);
02195
02196
02197 for (i = 0; i < n; i++) {
02198 const Vehicle *v = sort_list[i];
02199 CommandCost ret = DoCommand(v->tile, v->index, (service ? 1 : 0) | DEPOT_DONT_CANCEL, flags, GetCmdSendToDepot(type));
02200
02201
02202
02203
02204
02205 if (CmdSucceeded(ret) && !(flags & DC_EXEC)) {
02206 free((void*)sort_list);
02207 return CommandCost();
02208 }
02209 }
02210
02211 free((void*)sort_list);
02212 return (flags & DC_EXEC) ? CommandCost() : CMD_ERROR;
02213 }
02214
02221 uint8 CalcPercentVehicleFilled(Vehicle *v, StringID *color)
02222 {
02223 int count = 0;
02224 int max = 0;
02225 int cars = 0;
02226 int unloading = 0;
02227 bool loading = false;
02228
02229 assert(color != NULL);
02230
02231 const Vehicle *u = v;
02232 const Station *st = GetStation(v->last_station_visited);
02233
02234
02235 for (; v != NULL; v = v->Next()) {
02236 count += v->cargo.Count();
02237 max += v->cargo_cap;
02238 if (v->cargo_cap != 0) {
02239 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
02240 loading |= (u->current_order.flags & OFB_UNLOAD) == 0 && st->goods[v->cargo_type].days_since_pickup != 255;
02241 cars++;
02242 }
02243 }
02244
02245 if (unloading == 0 && loading) *color = STR_PERCENT_UP;
02246 else if (cars == unloading || !loading) *color = STR_PERCENT_DOWN;
02247 else *color = STR_PERCENT_UP_DOWN;
02248
02249
02250 if (max == 0) return 100;
02251
02252
02253 return (count * 100) / max;
02254 }
02255
02256 void VehicleEnterDepot(Vehicle *v)
02257 {
02258 switch (v->type) {
02259 case VEH_TRAIN:
02260 InvalidateWindowClasses(WC_TRAINS_LIST);
02261 if (!IsFrontEngine(v)) v = v->First();
02262 UpdateSignalsOnSegment(v->tile, INVALID_DIAGDIR, v->owner);
02263 v->load_unload_time_rem = 0;
02264
02265 for (Vehicle *u = v; u != NULL; u = u->Next()) ClrBit(u->u.rail.flags, VRF_TOGGLE_REVERSE);
02266 TrainConsistChanged(v, true);
02267 break;
02268
02269 case VEH_ROAD:
02270 InvalidateWindowClasses(WC_ROADVEH_LIST);
02271 if (!IsRoadVehFront(v)) v = v->First();
02272 break;
02273
02274 case VEH_SHIP:
02275 InvalidateWindowClasses(WC_SHIPS_LIST);
02276 v->u.ship.state = TRACK_BIT_DEPOT;
02277 RecalcShipStuff(v);
02278 break;
02279
02280 case VEH_AIRCRAFT:
02281 InvalidateWindowClasses(WC_AIRCRAFT_LIST);
02282 HandleAircraftEnterHangar(v);
02283 break;
02284 default: NOT_REACHED();
02285 }
02286
02287 if (v->type != VEH_TRAIN) {
02288
02289
02290 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
02291 }
02292 InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
02293
02294 v->vehstatus |= VS_HIDDEN;
02295 v->cur_speed = 0;
02296
02297 VehicleServiceInDepot(v);
02298
02299 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
02300
02301 if (v->current_order.type == OT_GOTO_DEPOT) {
02302 Order t;
02303
02304 InvalidateWindow(WC_VEHICLE_VIEW, v->index);
02305
02306 t = v->current_order;
02307 v->current_order.type = OT_DUMMY;
02308 v->current_order.flags = 0;
02309
02310 if (t.refit_cargo < NUM_CARGO) {
02311 CommandCost cost;
02312
02313 _current_player = v->owner;
02314 cost = DoCommand(v->tile, v->index, t.refit_cargo | t.refit_subtype << 8, DC_EXEC, GetCmdRefitVeh(v));
02315
02316 if (CmdFailed(cost)) {
02317 v->leave_depot_instantly = false;
02318 if (v->owner == _local_player) {
02319
02320 SetDParam(0, _vehicle_type_names[v->type]);
02321 SetDParam(1, v->unitnumber);
02322 AddNewsItem(STR_ORDER_REFIT_FAILED, NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, 0), v->index, 0);
02323 }
02324 } else if (v->owner == _local_player && cost.GetCost() != 0) {
02325 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
02326 }
02327 }
02328
02329 if (HasBit(t.flags, OF_PART_OF_ORDERS)) {
02330
02331 UpdateVehicleTimetable(v, true);
02332 v->cur_order_index++;
02333 } else if (HasBit(t.flags, OF_HALT_IN_DEPOT)) {
02334
02335 v->vehstatus |= VS_STOPPED;
02336 if (v->owner == _local_player) {
02337 StringID string;
02338
02339 switch (v->type) {
02340 case VEH_TRAIN: string = STR_8814_TRAIN_IS_WAITING_IN_DEPOT; break;
02341 case VEH_ROAD: string = STR_9016_ROAD_VEHICLE_IS_WAITING; break;
02342 case VEH_SHIP: string = STR_981C_SHIP_IS_WAITING_IN_DEPOT; break;
02343 case VEH_AIRCRAFT: string = STR_A014_AIRCRAFT_IS_WAITING_IN; break;
02344 default: NOT_REACHED(); string = STR_EMPTY;
02345 }
02346
02347 SetDParam(0, v->unitnumber);
02348 AddNewsItem(string, NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, 0), v->index, 0);
02349 }
02350 }
02351 }
02352 }
02353
02354 static bool IsUniqueVehicleName(const char *name)
02355 {
02356 const Vehicle *v;
02357 char buf[512];
02358
02359 FOR_ALL_VEHICLES(v) {
02360 switch (v->type) {
02361 case VEH_TRAIN:
02362 if (!IsTrainEngine(v)) continue;
02363 break;
02364
02365 case VEH_ROAD:
02366 if (!IsRoadVehFront(v)) continue;
02367 break;
02368
02369 case VEH_AIRCRAFT:
02370 if (!IsNormalAircraft(v)) continue;
02371 break;
02372
02373 case VEH_SHIP:
02374 break;
02375
02376 default:
02377 continue;
02378 }
02379
02380 SetDParam(0, v->index);
02381 GetString(buf, STR_VEHICLE_NAME, lastof(buf));
02382 if (strcmp(buf, name) == 0) return false;
02383 }
02384
02385 return true;
02386 }
02387
02394 CommandCost CmdNameVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
02395 {
02396 Vehicle *v;
02397
02398 if (!IsValidVehicleID(p1) || StrEmpty(_cmd_text)) return CMD_ERROR;
02399
02400 v = GetVehicle(p1);
02401
02402 if (!CheckOwnership(v->owner)) return CMD_ERROR;
02403
02404 if (!IsUniqueVehicleName(_cmd_text)) return_cmd_error(STR_NAME_MUST_BE_UNIQUE);
02405
02406 if (flags & DC_EXEC) {
02407 free(v->name);
02408 v->name = strdup(_cmd_text);
02409 ResortVehicleLists();
02410 MarkWholeScreenDirty();
02411 }
02412
02413 return CommandCost();
02414 }
02415
02416
02423 CommandCost CmdChangeServiceInt(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
02424 {
02425 Vehicle* v;
02426 uint16 serv_int = GetServiceIntervalClamped(p2);
02427
02428 if (serv_int != p2 || !IsValidVehicleID(p1)) return CMD_ERROR;
02429
02430 v = GetVehicle(p1);
02431
02432 if (!CheckOwnership(v->owner)) return CMD_ERROR;
02433
02434 if (flags & DC_EXEC) {
02435 v->service_interval = serv_int;
02436 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
02437 }
02438
02439 return CommandCost();
02440 }
02441
02442
02443 static Rect _old_vehicle_coords;
02444
02451 void BeginVehicleMove(const Vehicle *v)
02452 {
02453 _old_vehicle_coords.left = v->left_coord;
02454 _old_vehicle_coords.top = v->top_coord;
02455 _old_vehicle_coords.right = v->right_coord;
02456 _old_vehicle_coords.bottom = v->bottom_coord;
02457 }
02458
02465 void EndVehicleMove(const Vehicle *v)
02466 {
02467 MarkAllViewportsDirty(
02468 min(_old_vehicle_coords.left, v->left_coord),
02469 min(_old_vehicle_coords.top, v->top_coord),
02470 max(_old_vehicle_coords.right, v->right_coord) + 1,
02471 max(_old_vehicle_coords.bottom, v->bottom_coord) + 1
02472 );
02473 }
02474
02483 void MarkSingleVehicleDirty(const Vehicle *v)
02484 {
02485 MarkAllViewportsDirty(v->left_coord, v->top_coord, v->right_coord + 1, v->bottom_coord + 1);
02486 }
02487
02488
02489 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
02490 {
02491 static const int8 _delta_coord[16] = {
02492 -1,-1,-1, 0, 1, 1, 1, 0,
02493 -1, 0, 1, 1, 1, 0,-1,-1,
02494 };
02495
02496 int x = v->x_pos + _delta_coord[v->direction];
02497 int y = v->y_pos + _delta_coord[v->direction + 8];
02498
02499 GetNewVehiclePosResult gp;
02500 gp.x = x;
02501 gp.y = y;
02502 gp.old_tile = v->tile;
02503 gp.new_tile = TileVirtXY(x, y);
02504 return gp;
02505 }
02506
02507 static const Direction _new_direction_table[] = {
02508 DIR_N , DIR_NW, DIR_W ,
02509 DIR_NE, DIR_SE, DIR_SW,
02510 DIR_E , DIR_SE, DIR_S
02511 };
02512
02513 Direction GetDirectionTowards(const Vehicle* v, int x, int y)
02514 {
02515 Direction dir;
02516 DirDiff dirdiff;
02517 int i = 0;
02518
02519 if (y >= v->y_pos) {
02520 if (y != v->y_pos) i+=3;
02521 i+=3;
02522 }
02523
02524 if (x >= v->x_pos) {
02525 if (x != v->x_pos) i++;
02526 i++;
02527 }
02528
02529 dir = v->direction;
02530
02531 dirdiff = DirDifference(_new_direction_table[i], dir);
02532 if (dirdiff == DIRDIFF_SAME) return dir;
02533 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
02534 }
02535
02536 Trackdir GetVehicleTrackdir(const Vehicle* v)
02537 {
02538 if (v->vehstatus & VS_CRASHED) return INVALID_TRACKDIR;
02539
02540 switch (v->type) {
02541 case VEH_TRAIN:
02542 if (v->u.rail.track == TRACK_BIT_DEPOT)
02543 return DiagdirToDiagTrackdir(GetRailDepotDirection(v->tile));
02544
02545 if (v->u.rail.track == TRACK_BIT_WORMHOLE)
02546 return DiagdirToDiagTrackdir(DirToDiagDir(v->direction));
02547
02548 return TrackDirectionToTrackdir(FindFirstTrack(v->u.rail.track), v->direction);
02549
02550 case VEH_SHIP:
02551 if (v->IsInDepot())
02552
02553 return DiagdirToDiagTrackdir(GetShipDepotDirection(v->tile));
02554
02555 return TrackDirectionToTrackdir(FindFirstTrack(v->u.ship.state), v->direction);
02556
02557 case VEH_ROAD:
02558 if (v->IsInDepot())
02559 return DiagdirToDiagTrackdir(GetRoadDepotDirection(v->tile));
02560
02561 if (IsStandardRoadStopTile(v->tile))
02562 return DiagdirToDiagTrackdir(GetRoadStopDir(v->tile));
02563
02564 if (IsDriveThroughStopTile(v->tile)) return DiagdirToDiagTrackdir(DirToDiagDir(v->direction));
02565
02566
02567 if (!IsReversingRoadTrackdir((Trackdir)v->u.road.state)) return (Trackdir)v->u.road.state;
02568
02569
02570 return DiagdirToDiagTrackdir(DirToDiagDir(v->direction));
02571
02572
02573 default: return INVALID_TRACKDIR;
02574 }
02575 }
02576
02581 uint32 VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
02582 {
02583 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
02584 }
02585
02586 UnitID GetFreeUnitNumber(VehicleType type)
02587 {
02588 UnitID unit, max = 0;
02589 const Vehicle *u;
02590 static bool *cache = NULL;
02591 static UnitID gmax = 0;
02592
02593 switch (type) {
02594 case VEH_TRAIN: max = _patches.max_trains; break;
02595 case VEH_ROAD: max = _patches.max_roadveh; break;
02596 case VEH_SHIP: max = _patches.max_ships; break;
02597 case VEH_AIRCRAFT: max = _patches.max_aircraft; break;
02598 default: NOT_REACHED();
02599 }
02600
02601 if (max == 0) {
02602
02603
02604
02605
02606 return 1;
02607 }
02608
02609 if (max > gmax) {
02610 gmax = max;
02611 free(cache);
02612 cache = MallocT<bool>(max + 1);
02613 }
02614
02615
02616 memset(cache, 0, (max + 1) * sizeof(*cache));
02617
02618
02619 FOR_ALL_VEHICLES(u) {
02620 if (u->type == type && u->owner == _current_player && u->unitnumber != 0 && u->unitnumber <= max)
02621 cache[u->unitnumber] = true;
02622 }
02623
02624
02625 for (unit = 1; unit <= max; unit++) {
02626 if (!cache[unit]) break;
02627 }
02628
02629 return unit;
02630 }
02631
02632
02641 bool CanBuildVehicleInfrastructure(VehicleType type)
02642 {
02643 assert(IsPlayerBuildableVehicleType(type));
02644
02645 if (!IsValidPlayer(_current_player)) return false;
02646 if (_patches.always_build_infrastructure) return true;
02647
02648 UnitID max;
02649 switch (type) {
02650 case VEH_TRAIN: max = _patches.max_trains; break;
02651 case VEH_ROAD: max = _patches.max_roadveh; break;
02652 case VEH_SHIP: max = _patches.max_ships; break;
02653 case VEH_AIRCRAFT: max = _patches.max_aircraft; break;
02654 default: NOT_REACHED();
02655 }
02656
02657
02658 if (max > 0) {
02659
02660
02661 EngineID e;
02662 FOR_ALL_ENGINEIDS_OF_TYPE(e, type) {
02663 if (HasBit(GetEngine(e)->player_avail, _local_player)) return true;
02664 }
02665 return false;
02666 }
02667
02668
02669 const Vehicle *v;
02670 FOR_ALL_VEHICLES(v) {
02671 if (v->owner == _local_player && v->type == type) return true;
02672 }
02673
02674 return false;
02675 }
02676
02677
02678 const Livery *GetEngineLivery(EngineID engine_type, PlayerID player, EngineID parent_engine_type, const Vehicle *v)
02679 {
02680 const Player *p = GetPlayer(player);
02681 LiveryScheme scheme = LS_DEFAULT;
02682 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
02683
02684
02685
02686 if (p->livery[LS_DEFAULT].in_use && (_patches.liveries == 2 || (_patches.liveries == 1 && player == _local_player))) {
02687
02688 switch (GetEngine(engine_type)->type) {
02689 default: NOT_REACHED();
02690 case VEH_TRAIN: {
02691 const RailVehicleInfo *rvi = RailVehInfo(engine_type);
02692
02693 if (cargo_type == CT_INVALID) cargo_type = rvi->cargo_type;
02694 if (rvi->railveh_type == RAILVEH_WAGON) {
02695 if (!GetCargo(cargo_type)->is_freight) {
02696 if (parent_engine_type == INVALID_ENGINE) {
02697 scheme = LS_PASSENGER_WAGON_STEAM;
02698 } else {
02699 switch (RailVehInfo(parent_engine_type)->engclass) {
02700 default: NOT_REACHED();
02701 case EC_STEAM: scheme = LS_PASSENGER_WAGON_STEAM; break;
02702 case EC_DIESEL: scheme = LS_PASSENGER_WAGON_DIESEL; break;
02703 case EC_ELECTRIC: scheme = LS_PASSENGER_WAGON_ELECTRIC; break;
02704 case EC_MONORAIL: scheme = LS_PASSENGER_WAGON_MONORAIL; break;
02705 case EC_MAGLEV: scheme = LS_PASSENGER_WAGON_MAGLEV; break;
02706 }
02707 }
02708 } else {
02709 scheme = LS_FREIGHT_WAGON;
02710 }
02711 } else {
02712 bool is_mu = HasBit(EngInfo(engine_type)->misc_flags, EF_RAIL_IS_MU);
02713
02714 switch (rvi->engclass) {
02715 default: NOT_REACHED();
02716 case EC_STEAM: scheme = LS_STEAM; break;
02717 case EC_DIESEL: scheme = is_mu ? LS_DMU : LS_DIESEL; break;
02718 case EC_ELECTRIC: scheme = is_mu ? LS_EMU : LS_ELECTRIC; break;
02719 case EC_MONORAIL: scheme = LS_MONORAIL; break;
02720 case EC_MAGLEV: scheme = LS_MAGLEV; break;
02721 }
02722 }
02723 break;
02724 }
02725
02726 case VEH_ROAD: {
02727 const RoadVehicleInfo *rvi = RoadVehInfo(engine_type);
02728 if (cargo_type == CT_INVALID) cargo_type = rvi->cargo_type;
02729 if (HasBit(EngInfo(engine_type)->misc_flags, EF_ROAD_TRAM)) {
02730
02731 scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
02732 } else {
02733
02734 scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
02735 }
02736 break;
02737 }
02738
02739 case VEH_SHIP: {
02740 const ShipVehicleInfo *svi = ShipVehInfo(engine_type);
02741 if (cargo_type == CT_INVALID) cargo_type = svi->cargo_type;
02742 scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
02743 break;
02744 }
02745
02746 case VEH_AIRCRAFT: {
02747 const AircraftVehicleInfo *avi = AircraftVehInfo(engine_type);
02748 if (cargo_type == CT_INVALID) cargo_type = CT_PASSENGERS;
02749 switch (avi->subtype) {
02750 case AIR_HELI: scheme = LS_HELICOPTER; break;
02751 case AIR_CTOL: scheme = LS_SMALL_PLANE; break;
02752 case AIR_CTOL | AIR_FAST: scheme = LS_LARGE_PLANE; break;
02753 }
02754 break;
02755 }
02756 }
02757
02758
02759 if (!p->livery[scheme].in_use) scheme = LS_DEFAULT;
02760 }
02761
02762 return &p->livery[scheme];
02763 }
02764
02765
02766 static SpriteID GetEngineColourMap(EngineID engine_type, PlayerID player, EngineID parent_engine_type, const Vehicle *v)
02767 {
02768 SpriteID map = (v != NULL) ? v->colormap : PAL_NONE;
02769
02770
02771 if (map != PAL_NONE) return map;
02772
02773
02774 if (HasBit(EngInfo(engine_type)->callbackmask, CBM_VEHICLE_COLOUR_REMAP)) {
02775 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
02776
02777
02778 if (callback != CALLBACK_FAILED && callback != 0xC000) {
02779 map = GB(callback, 0, 14);
02780
02781
02782 if (!HasBit(callback, 14)) {
02783
02784 if (v != NULL) ((Vehicle*)v)->colormap = map;
02785 return map;
02786 }
02787 }
02788 }
02789
02790 bool twocc = HasBit(EngInfo(engine_type)->misc_flags, EF_USES_2CC);
02791
02792 if (map == PAL_NONE) map = twocc ? (SpriteID)SPR_2CCMAP_BASE : (SpriteID)PALETTE_RECOLOR_START;
02793
02794 const Livery *livery = GetEngineLivery(engine_type, player, parent_engine_type, v);
02795
02796 map += livery->colour1;
02797 if (twocc) map += livery->colour2 * 16;
02798
02799
02800 if (v != NULL) ((Vehicle*)v)->colormap = map;
02801 return map;
02802 }
02803
02804 SpriteID GetEnginePalette(EngineID engine_type, PlayerID player)
02805 {
02806 return GetEngineColourMap(engine_type, player, INVALID_ENGINE, NULL);
02807 }
02808
02809 SpriteID GetVehiclePalette(const Vehicle *v)
02810 {
02811 if (v->type == VEH_TRAIN) {
02812 return GetEngineColourMap(
02813 (v->u.rail.first_engine != INVALID_ENGINE && (UsesWagonOverride(v) || (IsArticulatedPart(v) && RailVehInfo(v->engine_type)->railveh_type != RAILVEH_WAGON))) ?
02814 v->u.rail.first_engine : v->engine_type,
02815 v->owner, v->u.rail.first_engine, v);
02816 }
02817
02818 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
02819 }
02820
02821 static uint8 _cargo_days;
02822 static uint16 _cargo_source;
02823 static uint32 _cargo_source_xy;
02824 static uint16 _cargo_count;
02825 static uint16 _cargo_paid_for;
02826 static Money _cargo_feeder_share;
02827 static uint32 _cargo_loaded_at_xy;
02828
02834 const SaveLoad *GetVehicleDescription(VehicleType vt)
02835 {
02837 static const SaveLoad _common_veh_desc[] = {
02838 SLE_VAR(Vehicle, subtype, SLE_UINT8),
02839
02840 SLE_REF(Vehicle, next, REF_VEHICLE_OLD),
02841 SLE_CONDVAR(Vehicle, name, SLE_NAME, 0, 83),
02842 SLE_CONDSTR(Vehicle, name, SLE_STR, 0, 84, SL_MAX_VERSION),
02843 SLE_CONDVAR(Vehicle, unitnumber, SLE_FILE_U8 | SLE_VAR_U16, 0, 7),
02844 SLE_CONDVAR(Vehicle, unitnumber, SLE_UINT16, 8, SL_MAX_VERSION),
02845 SLE_VAR(Vehicle, owner, SLE_UINT8),
02846 SLE_CONDVAR(Vehicle, tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
02847 SLE_CONDVAR(Vehicle, tile, SLE_UINT32, 6, SL_MAX_VERSION),
02848 SLE_CONDVAR(Vehicle, dest_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
02849 SLE_CONDVAR(Vehicle, dest_tile, SLE_UINT32, 6, SL_MAX_VERSION),
02850
02851 SLE_CONDVAR(Vehicle, x_pos, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
02852 SLE_CONDVAR(Vehicle, x_pos, SLE_UINT32, 6, SL_MAX_VERSION),
02853 SLE_CONDVAR(Vehicle, y_pos, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
02854 SLE_CONDVAR(Vehicle, y_pos, SLE_UINT32, 6, SL_MAX_VERSION),
02855 SLE_VAR(Vehicle, z_pos, SLE_UINT8),
02856 SLE_VAR(Vehicle, direction, SLE_UINT8),
02857
02858 SLE_CONDNULL(2, 0, 57),
02859 SLE_VAR(Vehicle, spritenum, SLE_UINT8),
02860 SLE_CONDNULL(5, 0, 57),
02861 SLE_VAR(Vehicle, engine_type, SLE_UINT16),
02862
02863 SLE_VAR(Vehicle, max_speed, SLE_UINT16),
02864 SLE_VAR(Vehicle, cur_speed, SLE_UINT16),
02865 SLE_VAR(Vehicle, subspeed, SLE_UINT8),
02866 SLE_VAR(Vehicle, acceleration, SLE_UINT8),
02867 SLE_VAR(Vehicle, progress, SLE_UINT8),
02868
02869 SLE_VAR(Vehicle, vehstatus, SLE_UINT8),
02870 SLE_CONDVAR(Vehicle, last_station_visited, SLE_FILE_U8 | SLE_VAR_U16, 0, 4),
02871 SLE_CONDVAR(Vehicle, last_station_visited, SLE_UINT16, 5, SL_MAX_VERSION),
02872
02873 SLE_VAR(Vehicle, cargo_type, SLE_UINT8),
02874 SLE_CONDVAR(Vehicle, cargo_subtype, SLE_UINT8, 35, SL_MAX_VERSION),
02875 SLEG_CONDVAR( _cargo_days, SLE_UINT8, 0, 67),
02876 SLEG_CONDVAR( _cargo_source, SLE_FILE_U8 | SLE_VAR_U16, 0, 6),
02877 SLEG_CONDVAR( _cargo_source, SLE_UINT16, 7, 67),
02878 SLEG_CONDVAR( _cargo_source_xy, SLE_UINT32, 44, 67),
02879 SLE_VAR(Vehicle, cargo_cap, SLE_UINT16),
02880 SLEG_CONDVAR( _cargo_count, SLE_UINT16, 0, 67),
02881 SLE_CONDLST(Vehicle, cargo, REF_CARGO_PACKET, 68, SL_MAX_VERSION),
02882
02883 SLE_VAR(Vehicle, day_counter, SLE_UINT8),
02884 SLE_VAR(Vehicle, tick_counter, SLE_UINT8),
02885 SLE_CONDVAR(Vehicle, running_ticks, SLE_UINT8, 88, SL_MAX_VERSION),
02886
02887 SLE_VAR(Vehicle, cur_order_index, SLE_UINT8),
02888 SLE_VAR(Vehicle, num_orders, SLE_UINT8),
02889
02890
02891
02892
02893 SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, type), SLE_UINT8, 0, 4),
02894 SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, dest), SLE_FILE_U8 | SLE_VAR_U16, 0, 4),
02895
02896
02897 SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, type), SLE_UINT8, 5, SL_MAX_VERSION),
02898 SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, flags), SLE_UINT8, 5, SL_MAX_VERSION),
02899 SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, dest), SLE_UINT16, 5, SL_MAX_VERSION),
02900
02901
02902 SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, refit_cargo), SLE_UINT8, 36, SL_MAX_VERSION),
02903 SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, refit_subtype), SLE_UINT8, 36, SL_MAX_VERSION),
02904
02905
02906 SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, wait_time), SLE_UINT16, 67, SL_MAX_VERSION),
02907 SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, travel_time), SLE_UINT16, 67, SL_MAX_VERSION),
02908
02909 SLE_REF(Vehicle, orders, REF_ORDER),
02910
02911 SLE_CONDVAR(Vehicle, age, SLE_FILE_U16 | SLE_VAR_I32, 0, 30),
02912 SLE_CONDVAR(Vehicle, age, SLE_INT32, 31, SL_MAX_VERSION),
02913 SLE_CONDVAR(Vehicle, max_age, SLE_FILE_U16 | SLE_VAR_I32, 0, 30),
02914 SLE_CONDVAR(Vehicle, max_age, SLE_INT32, 31, SL_MAX_VERSION),
02915 SLE_CONDVAR(Vehicle, date_of_last_service, SLE_FILE_U16 | SLE_VAR_I32, 0, 30),
02916 SLE_CONDVAR(Vehicle, date_of_last_service, SLE_INT32, 31, SL_MAX_VERSION),
02917 SLE_CONDVAR(Vehicle, service_interval, SLE_FILE_U16 | SLE_VAR_I32, 0, 30),
02918 SLE_CONDVAR(Vehicle, service_interval, SLE_INT32, 31, SL_MAX_VERSION),
02919 SLE_VAR(Vehicle, reliability, SLE_UINT16),
02920 SLE_VAR(Vehicle, reliability_spd_dec, SLE_UINT16),
02921 SLE_VAR(Vehicle, breakdown_ctr, SLE_UINT8),
02922 SLE_VAR(Vehicle, breakdown_delay, SLE_UINT8),
02923 SLE_VAR(Vehicle, breakdowns_since_last_service, SLE_UINT8),
02924 SLE_VAR(Vehicle, breakdown_chance, SLE_UINT8),
02925 SLE_CONDVAR(Vehicle, build_year, SLE_FILE_U8 | SLE_VAR_I32, 0, 30),
02926 SLE_CONDVAR(Vehicle, build_year, SLE_INT32, 31, SL_MAX_VERSION),
02927
02928 SLE_VAR(Vehicle, load_unload_time_rem, SLE_UINT16),
02929 SLEG_CONDVAR( _cargo_paid_for, SLE_UINT16, 45, SL_MAX_VERSION),
02930 SLE_CONDVAR(Vehicle, vehicle_flags, SLE_UINT8, 40, SL_MAX_VERSION),
02931
02932 SLE_CONDVAR(Vehicle, profit_this_year, SLE_FILE_I32 | SLE_VAR_I64, 0, 64),
02933 SLE_CONDVAR(Vehicle, profit_this_year, SLE_INT64, 65, SL_MAX_VERSION),
02934 SLE_CONDVAR(Vehicle, profit_last_year, SLE_FILE_I32 | SLE_VAR_I64, 0, 64),
02935 SLE_CONDVAR(Vehicle, profit_last_year, SLE_INT64, 65, SL_MAX_VERSION),
02936 SLEG_CONDVAR( _cargo_feeder_share, SLE_FILE_I32 | SLE_VAR_I64,51, 64),
02937 SLEG_CONDVAR( _cargo_feeder_share, SLE_INT64, 65, 67),
02938 SLEG_CONDVAR( _cargo_loaded_at_xy, SLE_UINT32, 51, 67),
02939 SLE_CONDVAR(Vehicle, value, SLE_FILE_I32 | SLE_VAR_I64, 0, 64),
02940 SLE_CONDVAR(Vehicle, value, SLE_INT64, 65, SL_MAX_VERSION),
02941
02942 SLE_CONDVAR(Vehicle, random_bits, SLE_UINT8, 2, SL_MAX_VERSION),
02943 SLE_CONDVAR(Vehicle, waiting_triggers, SLE_UINT8, 2, SL_MAX_VERSION),
02944
02945 SLE_CONDREF(Vehicle, next_shared, REF_VEHICLE, 2, SL_MAX_VERSION),
02946 SLE_CONDREF(Vehicle, prev_shared, REF_VEHICLE, 2, SL_MAX_VERSION),
02947
02948 SLE_CONDVAR(Vehicle, group_id, SLE_UINT16, 60, SL_MAX_VERSION),
02949
02950 SLE_CONDVAR(Vehicle, current_order_time, SLE_UINT32, 67, SL_MAX_VERSION),
02951 SLE_CONDVAR(Vehicle, lateness_counter, SLE_INT32, 67, SL_MAX_VERSION),
02952
02953
02954 SLE_CONDNULL(10, 2, SL_MAX_VERSION),
02955
02956 SLE_END()
02957 };
02958
02959
02960 static const SaveLoad _train_desc[] = {
02961 SLE_WRITEBYTE(Vehicle, type, VEH_TRAIN),
02962 SLE_VEH_INCLUDEX(),
02963 SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, crash_anim_pos), SLE_UINT16),
02964 SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, force_proceed), SLE_UINT8),
02965 SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, railtype), SLE_UINT8),
02966 SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, track), SLE_UINT8),
02967
02968 SLE_CONDVARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, flags), SLE_UINT8, 2, SL_MAX_VERSION),
02969 SLE_CONDNULL(2, 2, 59),
02970
02971 SLE_CONDNULL(2, 2, 19),
02972
02973 SLE_CONDNULL(11, 2, SL_MAX_VERSION),
02974
02975 SLE_END()
02976 };
02977
02978 static const SaveLoad _roadveh_desc[] = {
02979 SLE_WRITEBYTE(Vehicle, type, VEH_ROAD),
02980 SLE_VEH_INCLUDEX(),
02981 SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, state), SLE_UINT8),
02982 SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, frame), SLE_UINT8),
02983 SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, blocked_ctr), SLE_UINT16),
02984 SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, overtaking), SLE_UINT8),
02985 SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, overtaking_ctr), SLE_UINT8),
02986 SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, crashed_ctr), SLE_UINT16),
02987 SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, reverse_ctr), SLE_UINT8),
02988
02989 SLE_CONDREFX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, slot), REF_ROADSTOPS, 6, SL_MAX_VERSION),
02990 SLE_CONDNULL(1, 6, SL_MAX_VERSION),
02991 SLE_CONDVARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, slot_age), SLE_UINT8, 6, SL_MAX_VERSION),
02992
02993 SLE_CONDNULL(16, 2, SL_MAX_VERSION),
02994
02995 SLE_END()
02996 };
02997
02998 static const SaveLoad _ship_desc[] = {
02999 SLE_WRITEBYTE(Vehicle, type, VEH_SHIP),
03000 SLE_VEH_INCLUDEX(),
03001 SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleShip, state), SLE_UINT8),
03002
03003
03004 SLE_CONDNULL(16, 2, SL_MAX_VERSION),
03005
03006 SLE_END()
03007 };
03008
03009 static const SaveLoad _aircraft_desc[] = {
03010 SLE_WRITEBYTE(Vehicle, type, VEH_AIRCRAFT),
03011 SLE_VEH_INCLUDEX(),
03012 SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleAir, crashed_counter), SLE_UINT16),
03013 SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleAir, pos), SLE_UINT8),
03014
03015 SLE_CONDVARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleAir, targetairport), SLE_FILE_U8 | SLE_VAR_U16, 0, 4),
03016 SLE_CONDVARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleAir, targetairport), SLE_UINT16, 5, SL_MAX_VERSION),
03017
03018 SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleAir, state), SLE_UINT8),
03019
03020 SLE_CONDVARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleAir, previous_pos), SLE_UINT8, 2, SL_MAX_VERSION),
03021
03022
03023 SLE_CONDNULL(15, 2, SL_MAX_VERSION),
03024
03025 SLE_END()
03026 };
03027
03028 static const SaveLoad _special_desc[] = {
03029 SLE_WRITEBYTE(Vehicle, type, VEH_SPECIAL),
03030
03031 SLE_VAR(Vehicle, subtype, SLE_UINT8),
03032
03033 SLE_CONDVAR(Vehicle, tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
03034 SLE_CONDVAR(Vehicle, tile, SLE_UINT32, 6, SL_MAX_VERSION),
03035
03036 SLE_CONDVAR(Vehicle, x_pos, SLE_FILE_I16 | SLE_VAR_I32, 0, 5),
03037 SLE_CONDVAR(Vehicle, x_pos, SLE_INT32, 6, SL_MAX_VERSION),
03038 SLE_CONDVAR(Vehicle, y_pos, SLE_FILE_I16 | SLE_VAR_I32, 0, 5),
03039 SLE_CONDVAR(Vehicle, y_pos, SLE_INT32, 6, SL_MAX_VERSION),
03040 SLE_VAR(Vehicle, z_pos, SLE_UINT8),
03041
03042 SLE_VAR(Vehicle, cur_image, SLE_UINT16),
03043 SLE_CONDNULL(5, 0, 57),
03044 SLE_VAR(Vehicle, progress, SLE_UINT8),
03045 SLE_VAR(Vehicle, vehstatus, SLE_UINT8),
03046
03047 SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleSpecial, animation_state), SLE_UINT16),
03048 SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleSpecial, animation_substate), SLE_UINT8),
03049
03050 SLE_CONDVAR(Vehicle, spritenum, SLE_UINT8, 2, SL_MAX_VERSION),
03051
03052
03053 SLE_CONDNULL(15, 2, SL_MAX_VERSION),
03054
03055 SLE_END()
03056 };
03057
03058 static const SaveLoad _disaster_desc[] = {
03059 SLE_WRITEBYTE(Vehicle, type, VEH_DISASTER),
03060
03061 SLE_REF(Vehicle, next, REF_VEHICLE_OLD),
03062
03063 SLE_VAR(Vehicle, subtype, SLE_UINT8),
03064 SLE_CONDVAR(Vehicle, tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
03065 SLE_CONDVAR(Vehicle, tile, SLE_UINT32, 6, SL_MAX_VERSION),
03066 SLE_CONDVAR(Vehicle, dest_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
03067 SLE_CONDVAR(Vehicle, dest_tile, SLE_UINT32, 6, SL_MAX_VERSION),
03068
03069 SLE_CONDVAR(Vehicle, x_pos, SLE_FILE_I16 | SLE_VAR_I32, 0, 5),
03070 SLE_CONDVAR(Vehicle, x_pos, SLE_INT32, 6, SL_MAX_VERSION),
03071 SLE_CONDVAR(Vehicle, y_pos, SLE_FILE_I16 | SLE_VAR_I32, 0, 5),
03072 SLE_CONDVAR(Vehicle, y_pos, SLE_INT32, 6, SL_MAX_VERSION),
03073 SLE_VAR(Vehicle, z_pos, SLE_UINT8),
03074 SLE_VAR(Vehicle, direction, SLE_UINT8),
03075
03076 SLE_CONDNULL(5, 0, 57),
03077 SLE_VAR(Vehicle, owner, SLE_UINT8),
03078 SLE_VAR(Vehicle, vehstatus, SLE_UINT8),
03079 SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, dest), SLE_FILE_U8 | SLE_VAR_U16, 0, 4),
03080 SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, dest), SLE_UINT16, 5, SL_MAX_VERSION),
03081
03082 SLE_VAR(Vehicle, cur_image, SLE_UINT16),
03083 SLE_CONDVAR(Vehicle, age, SLE_FILE_U16 | SLE_VAR_I32, 0, 30),
03084 SLE_CONDVAR(Vehicle, age, SLE_INT32, 31, SL_MAX_VERSION),
03085 SLE_VAR(Vehicle, tick_counter, SLE_UINT8),
03086
03087 SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleDisaster, image_override), SLE_UINT16),
03088 SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleDisaster, big_ufo_destroyer_target), SLE_UINT16),
03089
03090
03091 SLE_CONDNULL(16, 2, SL_MAX_VERSION),
03092
03093 SLE_END()
03094 };
03095
03096
03097 static const SaveLoad *_veh_descs[] = {
03098 _train_desc,
03099 _roadveh_desc,
03100 _ship_desc,
03101 _aircraft_desc,
03102 _special_desc,
03103 _disaster_desc,
03104 _common_veh_desc,
03105 };
03106
03107 return _veh_descs[vt];
03108 }
03109
03111 static void Save_VEHS()
03112 {
03113 Vehicle *v;
03114
03115 FOR_ALL_VEHICLES(v) {
03116 SlSetArrayIndex(v->index);
03117 SlObject(v, GetVehicleDescription(v->type));
03118 }
03119 }
03120
03122 static void Load_VEHS()
03123 {
03124 int index;
03125 Vehicle *v;
03126
03127 _cargo_count = 0;
03128
03129 while ((index = SlIterateArray()) != -1) {
03130 Vehicle *v;
03131 VehicleType vtype = (VehicleType)SlReadByte();
03132
03133 switch (vtype) {
03134 case VEH_TRAIN: v = new (index) Train(); break;
03135 case VEH_ROAD: v = new (index) RoadVehicle(); break;
03136 case VEH_SHIP: v = new (index) Ship(); break;
03137 case VEH_AIRCRAFT: v = new (index) Aircraft(); break;
03138 case VEH_SPECIAL: v = new (index) SpecialVehicle(); break;
03139 case VEH_DISASTER: v = new (index) DisasterVehicle(); break;
03140 case VEH_INVALID: v = new (index) InvalidVehicle(); break;
03141 default: NOT_REACHED();
03142 }
03143
03144 SlObject(v, GetVehicleDescription(vtype));
03145
03146 if (_cargo_count != 0 && IsPlayerBuildableVehicleType(v)) {
03147
03148 CargoPacket *cp = new CargoPacket();
03149 cp->source = _cargo_source;
03150 cp->source_xy = _cargo_source_xy;
03151 cp->count = _cargo_count;
03152 cp->days_in_transit = _cargo_days;
03153 cp->feeder_share = _cargo_feeder_share;
03154 cp->loaded_at_xy = _cargo_loaded_at_xy;
03155 v->cargo.Append(cp);
03156 }
03157
03158
03159 if (CheckSavegameVersion(5) && v->last_station_visited == 0xFF)
03160 v->last_station_visited = INVALID_STATION;
03161
03162 if (CheckSavegameVersion(5)) {
03163
03164
03165 v->current_order.flags = (v->current_order.type & 0xF0) >> 4;
03166 v->current_order.type.m_val &= 0x0F;
03167 }
03168
03169
03170 if (CheckSavegameVersion(60)) v->group_id = DEFAULT_GROUP;
03171 }
03172
03173
03174 if (CheckSavegameVersionOldStyle(5, 2)) {
03175 FOR_ALL_VEHICLES(v) {
03176 Vehicle *u;
03177
03178 FOR_ALL_VEHICLES_FROM(u, v->index + 1) {
03179
03180
03181 if (v->orders == u->orders) {
03182 v->next_shared = u;
03183 u->prev_shared = v;
03184 break;
03185 }
03186 }
03187 }
03188 }
03189 }
03190
03191 extern const ChunkHandler _veh_chunk_handlers[] = {
03192 { 'VEHS', Save_VEHS, Load_VEHS, CH_SPARSE_ARRAY | CH_LAST},
03193 };
03194
03195 void Vehicle::BeginLoading()
03196 {
03197 assert(IsTileType(tile, MP_STATION) || type == VEH_SHIP);
03198
03199 if (this->current_order.type == OT_GOTO_STATION &&
03200 this->current_order.dest == this->last_station_visited) {
03201
03202
03203 this->current_order.flags &= OFB_FULL_LOAD | OFB_UNLOAD | OFB_TRANSFER;
03204
03205
03206
03207
03208
03209
03210 this->current_order.flags |= OFB_NON_STOP;
03211 UpdateVehicleTimetable(this, true);
03212 } else {
03213
03214 this->current_order.flags = 0;
03215 }
03216
03217 current_order.type = OT_LOADING;
03218 GetStation(this->last_station_visited)->loading_vehicles.push_back(this);
03219
03220 VehiclePayment(this);
03221
03222 InvalidateWindow(this->GetVehicleListWindowClass(), this->owner);
03223 InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
03224 InvalidateWindow(WC_VEHICLE_DETAILS, this->index);
03225 InvalidateWindow(WC_STATION_VIEW, this->last_station_visited);
03226
03227 GetStation(this->last_station_visited)->MarkTilesDirty(true);
03228 this->MarkDirty();
03229 }
03230
03231 void Vehicle::LeaveStation()
03232 {
03233 assert(current_order.type == OT_LOADING);
03234
03235
03236 if (current_order.flags & OFB_NON_STOP) UpdateVehicleTimetable(this, false);
03237
03238 current_order.type = OT_LEAVESTATION;
03239 current_order.flags = 0;
03240 GetStation(this->last_station_visited)->loading_vehicles.remove(this);
03241
03242 HideFillingPercent(&this->fill_percent_te_id);
03243 }
03244
03245
03246 void Vehicle::HandleLoading(bool mode)
03247 {
03248 switch (this->current_order.type) {
03249 case OT_LOADING: {
03250 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
03251
03252
03253 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) ||
03254 (_patches.timetabling && this->current_order_time < wait_time)) return;
03255
03256 this->PlayLeaveStationSound();
03257
03258 Order b = this->current_order;
03259 this->LeaveStation();
03260
03261
03262 if (!(b.flags & OFB_NON_STOP)) return;
03263 break;
03264 }
03265
03266 case OT_DUMMY: break;
03267
03268 default: return;
03269 }
03270
03271 this->cur_order_index++;
03272 InvalidateVehicleOrder(this);
03273 }
03274
03275 void Vehicle::SetNext(Vehicle *next)
03276 {
03277 if (this->next != NULL) {
03278
03279 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
03280 v->first = this->next;
03281 }
03282 this->next->previous = NULL;
03283 }
03284
03285 this->next = next;
03286
03287 if (this->next != NULL) {
03288
03289 if (this->next->previous != NULL) this->next->previous->next = NULL;
03290 this->next->previous = this;
03291 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
03292 v->first = this->first;
03293 }
03294 }
03295 }
03296
03297 void SpecialVehicle::UpdateDeltaXY(Direction direction)
03298 {
03299 this->x_offs = 0;
03300 this->y_offs = 0;
03301 this->sprite_width = 1;
03302 this->sprite_height = 1;
03303 this->z_height = 1;
03304 }
03305
03306 void StopAllVehicles()
03307 {
03308 Vehicle *v;
03309 FOR_ALL_VEHICLES(v) {
03310
03311
03312 v->vehstatus |= VS_STOPPED;
03313 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
03314 InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
03315 }
03316 }