diff options
-rw-r--r-- | src/direction_func.h | 11 | ||||
-rw-r--r-- | src/rail_cmd.cpp | 7 | ||||
-rw-r--r-- | src/saveload/afterload.cpp | 3 | ||||
-rw-r--r-- | src/saveload/saveload.cpp | 3 | ||||
-rw-r--r-- | src/saveload/saveload_internal.h | 1 | ||||
-rw-r--r-- | src/saveload/vehicle_sl.cpp | 90 | ||||
-rw-r--r-- | src/train.h | 12 | ||||
-rw-r--r-- | src/train_cmd.cpp | 153 | ||||
-rw-r--r-- | src/vehicle.cpp | 7 | ||||
-rw-r--r-- | src/vehicle_base.h | 2 |
10 files changed, 230 insertions, 59 deletions
diff --git a/src/direction_func.h b/src/direction_func.h index f3b714a8e..bf92a9ad6 100644 --- a/src/direction_func.h +++ b/src/direction_func.h @@ -239,4 +239,15 @@ static inline bool IsValidAxis(Axis d) return d < AXIS_END; } +/** + * Checks if a given Direction is diagonal. + * + * @param dir The given direction. + * @return True if the direction is diagonal. + */ +static inline bool IsDiagonalDirection(Direction dir) +{ + return (dir & 1) != 0; +} + #endif /* DIRECTION_FUNC_H */ diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index 6196d78af..ed0362935 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -2711,7 +2711,7 @@ static const int8 _deltacoord_leaveoffset[8] = { int TicksToLeaveDepot(const Train *v) { DiagDirection dir = GetRailDepotDirection(v->tile); - int length = v->gcache.cached_veh_length; + int length = v->CalcNextVehicleOffset(); switch (dir) { case DIAGDIR_NE: return ((int)(v->x_pos & 0x0F) - ((_fractcoords_enter[dir] & 0x0F) - (length + 1))); @@ -2738,9 +2738,8 @@ static VehicleEnterTileStatus VehicleEnter_Track(Vehicle *u, TileIndex tile, int /* depot direction */ DiagDirection dir = GetRailDepotDirection(tile); - /* calculate the point where the following wagon should be activated - * this depends on the length of the current vehicle */ - int length = v->gcache.cached_veh_length; + /* Calculate the point where the following wagon should be activated. */ + int length = v->CalcNextVehicleOffset(); byte fract_coord_leave = ((_fractcoords_enter[dir] & 0x0F) + // x diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 2fc3e0e5e..97f35e6d4 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -2667,6 +2667,9 @@ bool AfterLoadGame() } } + /* The center of train vehicles was changed, fix up spacing. */ + if (IsSavegameVersionBefore(164)) FixupTrainLengths(); + /* When any NewGRF has been changed the availability of some vehicles might * have been changed too. e->company_avail must be set to 0 in that case * which is done by StartupEngines(). */ diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index c2c158824..e74659d66 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -228,8 +228,9 @@ * 161 22567 * 162 22713 * 163 22767 + * 164 23290 */ -extern const uint16 SAVEGAME_VERSION = 163; ///< Current savegame version of OpenTTD. +extern const uint16 SAVEGAME_VERSION = 164; ///< Current savegame version of OpenTTD. SavegameType _savegame_type; ///< type of savegame we are loading diff --git a/src/saveload/saveload_internal.h b/src/saveload/saveload_internal.h index b400861b4..613ff262a 100644 --- a/src/saveload/saveload_internal.h +++ b/src/saveload/saveload_internal.h @@ -27,6 +27,7 @@ void MoveWaypointsToBaseStations(); const SaveLoad *GetBaseStationDescription(); void AfterLoadVehicles(bool part_of_load); +void FixupTrainLengths(); void AfterLoadStations(); void AfterLoadRoadStops(); void AfterLoadLabelMaps(); diff --git a/src/saveload/vehicle_sl.cpp b/src/saveload/vehicle_sl.cpp index 4dc0a7fe2..1e47094eb 100644 --- a/src/saveload/vehicle_sl.cpp +++ b/src/saveload/vehicle_sl.cpp @@ -249,8 +249,6 @@ void AfterLoadVehicles(bool part_of_load) if (v->Next() != NULL) v->Next()->previous = v; if (v->NextShared() != NULL) v->NextShared()->previous_shared = v; - v->UpdateDeltaXY(v->direction); - if (part_of_load) v->fill_percent_te_id = INVALID_TE_ID; v->first = NULL; if (v->IsGroundVehicle()) v->GetGroundVehicleCache()->first_engine = INVALID_ENGINE; @@ -442,11 +440,99 @@ void AfterLoadVehicles(bool part_of_load) default: break; } + v->UpdateDeltaXY(v->direction); v->coord.left = INVALID_COORD; VehicleMove(v, false); } } +bool TrainController(Train *v, Vehicle *nomove, bool reverse = true); // From train_cmd.cpp +void ReverseTrainDirection(Train *v); +void ReverseTrainSwapVeh(Train *v, int l, int r); + +/** Fixup old train spacing. */ +void FixupTrainLengths() +{ + /* Vehicle center was moved from 4 units behind the front to half the length + * behind the front. Move vehicles so they end up on the same spot. */ + Vehicle *v; + FOR_ALL_VEHICLES(v) { + if (v->type == VEH_TRAIN && v->IsPrimaryVehicle()) { + /* The vehicle center is now more to the front depending on vehicle length, + * so we need to move all vehicles forward to cover the difference to the + * old center, otherwise wagon spacing in trains would be broken upon load. */ + for (Train *u = Train::From(v); u != NULL; u = u->Next()) { + if (u->track == TRACK_BIT_DEPOT || (u->vehstatus & VS_CRASHED)) continue; + + Train *next = u->Next(); + + /* Try to pull the vehicle half its length forward. */ + int diff = (VEHICLE_LENGTH - u->gcache.cached_veh_length) / 2; + int done; + for (done = 0; done < diff; done++) { + if (!TrainController(u, next, false)) break; + } + + if (next != NULL && done < diff && u->IsFrontEngine()) { + /* Pulling the front vehicle forwards failed, we either encountered a dead-end + * or a red signal. To fix this, we try to move the whole train the required + * space backwards and re-do the fix up of the front vehicle. */ + + /* Ignore any signals when backtracking. */ + TrainForceProceeding old_tfp = u->force_proceed; + u->force_proceed = TFP_SIGNAL; + + /* Swap start<>end, start+1<>end-1, ... */ + int r = CountVehiclesInChain(u) - 1; // number of vehicles - 1 + int l = 0; + do ReverseTrainSwapVeh(u, l++, r--); while (l <= r); + + /* We moved the first vehicle which is now the last. Move it back to the + * original position as we will fix up the last vehicle later in the loop. */ + for (int i = 0; i < done; i++) TrainController(u->Last(), NULL); + + /* Move the train backwards to get space for the first vehicle. As the stopping + * distance from a line end is rounded up, move the train one unit more to cater + * for front vehicles with odd lengths. */ + int moved; + for (moved = 0; moved < diff + 1; moved++) { + if (!TrainController(u, NULL, false)) break; + } + + /* Swap start<>end, start+1<>end-1, ... again. */ + r = CountVehiclesInChain(u) - 1; // number of vehicles - 1 + l = 0; + do ReverseTrainSwapVeh(u, l++, r--); while (l <= r); + + u->force_proceed = old_tfp; + + /* Tracks are too short to fix the train length. The player has to fix the + * train in a depot. Bail out so we don't damage the vehicle chain any more. */ + if (moved < diff + 1) break; + + /* Re-do the correction for the first vehicle. */ + for (done = 0; done < diff; done++) TrainController(u, next, false); + + /* We moved one unit more backwards than needed for even-length front vehicles, + * try to move that unit forward again. We don't care if this step fails. */ + TrainController(u, NULL, false); + } + + /* If the next wagon is still in a depot, check if it shouldn't be outside already. */ + if (next != NULL && next->track == TRACK_BIT_DEPOT) { + int d = TicksToLeaveDepot(u); + if (d <= 0) { + /* Next vehicle should have left the depot already, show it and pull forward. */ + next->vehstatus &= ~VS_HIDDEN; + next->track = TrackToTrackBits(GetRailDepotTrack(next->tile)); + for (int i = 0; i >= d; i--) TrainController(next, NULL); + } + } + } + } + } +} + static uint8 _cargo_days; static uint16 _cargo_source; static uint32 _cargo_source_xy; diff --git a/src/train.h b/src/train.h index ad273e035..9bc40fe97 100644 --- a/src/train.h +++ b/src/train.h @@ -148,6 +148,18 @@ struct Train : public GroundVehicle<Train, VEH_TRAIN> { return v; } + /** + * Calculate the offset from this vehicle's center to the following center taking the vehicle lengths into account. + * @return Offset from center to center. + */ + int CalcNextVehicleOffset() const + { + /* For vehicles with odd lengths the part before the center will be one unit + * longer than the part after the center. This means we have to round up the + * length of the next vehicle but may not round the length of the current + * vehicle. */ + return this->gcache.cached_veh_length / 2 + (this->Next() != NULL ? this->Next()->gcache.cached_veh_length + 1 : 0) / 2; + } protected: // These functions should not be called outside acceleration code. diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 2ba40618e..bd245cafd 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -41,8 +41,8 @@ #include "table/train_cmd.h" static Track ChooseTrainTrack(Train *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool force_res, bool *got_reservation, bool mark_stuck); -static bool TrainCheckIfLineEnds(Train *v); -static void TrainController(Train *v, Vehicle *nomove); +static bool TrainCheckIfLineEnds(Train *v, bool reverse = true); +bool TrainController(Train *v, Vehicle *nomove, bool reverse = true); // Also used in vehicle_sl.cpp. static TileIndex TrainApproachingCrossingTile(const Train *v); static void CheckIfTrainNeedsService(Train *v); static void CheckNextTrainTile(Train *v); @@ -95,7 +95,7 @@ void CheckTrainsLengths() for (const Train *u = v, *w = v->Next(); w != NULL; u = w, w = w->Next()) { if (u->track != TRACK_BIT_DEPOT) { if ((w->track != TRACK_BIT_DEPOT && - max(abs(u->x_pos - w->x_pos), abs(u->y_pos - w->y_pos)) != u->gcache.cached_veh_length) || + max(abs(u->x_pos - w->x_pos), abs(u->y_pos - w->y_pos)) != u->CalcNextVehicleOffset()) || (w->track == TRACK_BIT_DEPOT && TicksToLeaveDepot(u) <= 0)) { SetDParam(0, v->index); SetDParam(1, v->owner); @@ -305,11 +305,8 @@ int GetTrainStopLocation(StationID station_id, TileIndex tile, const Train *v, i } /* Subtract half the front vehicle length of the train so we get the real - * stop location of the train. - * Actually, the center of all vehicles is half a normal vehicle's length - * from the front of the vehicle, so even in case the vehicle is 1/8th - * long, the center is still at 1/2 of VEHICLE_LENGTH. Basically FS#3569. */ - return stop - VEHICLE_LENGTH / 2; + * stop location of the train. */ + return stop - (v->gcache.cached_veh_length + 1) / 2; } @@ -1382,25 +1379,66 @@ CommandCost CmdSellRailWagon(DoCommandFlag flags, Vehicle *t, uint16 data, uint3 void Train::UpdateDeltaXY(Direction direction) { -#define MKIT(a, b, c, d) ((a & 0xFF) << 24) | ((b & 0xFF) << 16) | ((c & 0xFF) << 8) | ((d & 0xFF) << 0) - static const uint32 _delta_xy_table[8] = { - MKIT(3, 3, -1, -1), - MKIT(3, 7, -1, -3), - MKIT(3, 3, -1, -1), - MKIT(7, 3, -3, -1), - MKIT(3, 3, -1, -1), - MKIT(3, 7, -1, -3), - MKIT(3, 3, -1, -1), - MKIT(7, 3, -3, -1), - }; -#undef MKIT + /* Set common defaults. */ + this->x_offs = -1; + this->y_offs = -1; + this->x_extent = 3; + this->y_extent = 3; + this->z_extent = 6; + this->x_bb_offs = 0; + this->y_bb_offs = 0; + + if (!IsDiagonalDirection(direction)) { + static const int _sign_table[] = + { + // x, y + -1, -1, // DIR_N + -1, 1, // DIR_E + 1, 1, // DIR_S + 1, -1, // DIR_W + }; + + int half_shorten = (VEHICLE_LENGTH - this->gcache.cached_veh_length) / 2; + + /* For all straight directions, move the bound box to the centre of the vehicle, but keep the size. */ + this->x_offs -= half_shorten * _sign_table[direction]; + this->y_offs -= half_shorten * _sign_table[direction + 1]; + this->x_extent += this->x_bb_offs = half_shorten * _sign_table[direction]; + this->y_extent += this->y_bb_offs = half_shorten * _sign_table[direction + 1]; + } else { + switch (direction) { + /* Shorten southern corner of the bounding box according the vehicle length + * and center the bounding box on the vehicle. */ + case DIR_NE: + this->x_offs = 1 - (this->gcache.cached_veh_length + 1) / 2; + this->x_extent = this->gcache.cached_veh_length - 1; + this->x_bb_offs = -1; + break; - uint32 x = _delta_xy_table[direction]; - this->x_offs = GB(x, 0, 8); - this->y_offs = GB(x, 8, 8); - this->x_extent = GB(x, 16, 8); - this->y_extent = GB(x, 24, 8); - this->z_extent = 6; + case DIR_NW: + this->y_offs = 1 - (this->gcache.cached_veh_length + 1) / 2; + this->y_extent = this->gcache.cached_veh_length - 1; + this->y_bb_offs = -1; + break; + + /* Move northern corner of the bounding box down according to vehicle length + * and center the bounding box on the vehicle. */ + case DIR_SW: + this->x_offs = 1 + (this->gcache.cached_veh_length + 1) / 2 - VEHICLE_LENGTH; + this->x_extent = VEHICLE_LENGTH - 1; + this->x_bb_offs = VEHICLE_LENGTH - this->gcache.cached_veh_length - 1; + break; + + case DIR_SE: + this->y_offs = 1 + (this->gcache.cached_veh_length + 1) / 2 - VEHICLE_LENGTH; + this->y_extent = VEHICLE_LENGTH - 1; + this->y_bb_offs = VEHICLE_LENGTH - this->gcache.cached_veh_length - 1; + break; + + default: + NOT_REACHED(); + } + } } /** Mark a train as stuck and stop it if it isn't stopped right now. */ @@ -1478,7 +1516,7 @@ static void UpdateStatusAfterSwap(Train *v) v->UpdateViewport(true, true); } -static void ReverseTrainSwapVeh(Train *v, int l, int r) +void ReverseTrainSwapVeh(Train *v, int l, int r) { Train *a, *b; @@ -1626,7 +1664,7 @@ static void AdvanceWagonsBeforeSwap(Train *v) last = last->Previous(); first = first->Next(); - int differential = base->gcache.cached_veh_length - last->gcache.cached_veh_length; + int differential = base->CalcNextVehicleOffset() - last->CalcNextVehicleOffset(); /* do not update images now * negative differential will be handled in AdvanceWagonsAfterSwap() */ @@ -1686,7 +1724,7 @@ static void AdvanceWagonsAfterSwap(Train *v) last = last->Previous(); first = first->Next(); - int differential = last->gcache.cached_veh_length - base->gcache.cached_veh_length; + int differential = last->CalcNextVehicleOffset() - base->CalcNextVehicleOffset(); /* do not update images now */ for (int i = 0; i < differential; i++) TrainController(first, (nomove ? last->Next() : NULL)); @@ -1697,7 +1735,7 @@ static void AdvanceWagonsAfterSwap(Train *v) } -static void ReverseTrainDirection(Train *v) +void ReverseTrainDirection(Train *v) { if (IsRailDepotTile(v->tile)) { InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); @@ -2840,7 +2878,8 @@ static Vehicle *FindTrainCollideEnum(Vehicle *v, void *data) if (hash & ~15) return NULL; /* Slower check using multiplication */ - if (x_diff * x_diff + y_diff * y_diff > 25) return NULL; + int min_diff = (Train::From(v)->gcache.cached_veh_length + 1) / 2 + (tcc->v->gcache.cached_veh_length + 1) / 2 - 1; + if (x_diff * x_diff + y_diff * y_diff > min_diff * min_diff) return NULL; /* Happens when there is a train under bridge next to bridge head */ if (abs(v->z_pos - tcc->v->z_pos) > 5) return NULL; @@ -2906,7 +2945,14 @@ static Vehicle *CheckTrainAtSignal(Vehicle *v, void *data) return t; } -static void TrainController(Train *v, Vehicle *nomove) +/** + * Move a vehicle chain one movement stop forwards. + * @param v First vehicle to move. + * @param nomove Stop moving this and all following vehicles. + * @param reverse Set to false to not execute the vehicle reversing. This does not change any other logic. + * @return True if the vehicle could be moved forward, false otherwise. + */ +bool TrainController(Train *v, Vehicle *nomove, bool reverse) { Train *first = v->First(); Train *prev; @@ -2930,7 +2976,7 @@ static void TrainController(Train *v, Vehicle *nomove) /* Not inside depot */ /* Reverse when we are at the end of the track already, do not move to the new position */ - if (v->IsFrontEngine() && !TrainCheckIfLineEnds(v)) return; + if (v->IsFrontEngine() && !TrainCheckIfLineEnds(v, reverse)) return false; uint32 r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y); if (HasBit(r, VETS_CANNOT_ENTER)) { @@ -2997,13 +3043,13 @@ static void TrainController(Train *v, Vehicle *nomove) Trackdir i = FindFirstTrackdir(trackdirbits); /* Don't handle stuck trains here. */ - if (HasBit(v->flags, VRF_TRAIN_STUCK)) return; + if (HasBit(v->flags, VRF_TRAIN_STUCK)) return false; if (!HasSignalOnTrackdir(gp.new_tile, ReverseTrackdir(i))) { v->cur_speed = 0; v->subspeed = 0; v->progress = 255 - 100; - if (!_settings_game.pf.reverse_at_signals || ++v->wait_counter < _settings_game.pf.wait_oneway_signal * 20) return; + if (!_settings_game.pf.reverse_at_signals || ++v->wait_counter < _settings_game.pf.wait_oneway_signal * 20) return false; } else if (HasSignalOnTrackdir(gp.new_tile, i)) { v->cur_speed = 0; v->subspeed = 0; @@ -3015,7 +3061,7 @@ static void TrainController(Train *v, Vehicle *nomove) exitdir = ReverseDiagDir(exitdir); /* check if a train is waiting on the other side */ - if (!HasVehicleOnPos(o_tile, &exitdir, &CheckTrainAtSignal)) return; + if (!HasVehicleOnPos(o_tile, &exitdir, &CheckTrainAtSignal)) return false; } } @@ -3026,7 +3072,7 @@ static void TrainController(Train *v, Vehicle *nomove) if (!_settings_game.pf.reverse_at_signals && !HasOnewaySignalBlockingTrackdir(gp.new_tile, i) && UpdateSignalsOnSegment(v->tile, enterdir, v->owner) == SIGSEG_PBS) { v->wait_counter = 0; - return; + return false; } goto reverse_train_direction; } else { @@ -3209,17 +3255,21 @@ static void TrainController(Train *v, Vehicle *nomove) if (direction_changed) first->tcache.cached_max_curve_speed = first->GetCurveSpeedLimit(); - return; + return true; invalid_rail: /* We've reached end of line?? */ if (prev != NULL) error("Disconnecting train"); reverse_train_direction: - v->wait_counter = 0; - v->cur_speed = 0; - v->subspeed = 0; - ReverseTrainDirection(v); + if (reverse) { + v->wait_counter = 0; + v->cur_speed = 0; + v->subspeed = 0; + ReverseTrainDirection(v); + } + + return false; } /** @@ -3389,9 +3439,10 @@ static const uint16 _breakdown_speeds[16] = { * * @param v front train engine * @param signal not line end, just a red signal + * @param reverse Set to false to not execute the vehicle reversing. This does not change any other logic. * @return true iff we did NOT have to reverse */ -static bool TrainApproachingLineEnd(Train *v, bool signal) +static bool TrainApproachingLineEnd(Train *v, bool signal, bool reverse) { /* Calc position within the current tile */ uint x = v->x_pos & 0xF; @@ -3413,13 +3464,12 @@ static bool TrainApproachingLineEnd(Train *v, bool signal) /* Do not reverse when approaching red signal. Make sure the vehicle's front * does not cross the tile boundary when we do reverse, but as the vehicle's * location is based on their center, use half a vehicle's length as offset. - * Actually, the center of all vehicles is half a normal vehicle's length - * from the front of the vehicle, so even in case the vehicle is 1/8th - * long, the center is still at 1/2 of VEHICLE_LENGTH. Basically FS#3569. */ - if (!signal && x + VEHICLE_LENGTH / 2 >= TILE_SIZE) { + * Multiply the half-length by two for straight directions to compensate that + * we only get odd x offsets there. */ + if (!signal && x + (v->gcache.cached_veh_length + 1) / 2 * (IsDiagonalDirection(v->direction) ? 1 : 2) >= TILE_SIZE) { /* we are too near the tile end, reverse now */ v->cur_speed = 0; - ReverseTrainDirection(v); + if (reverse) ReverseTrainDirection(v); return false; } @@ -3491,9 +3541,10 @@ static TileIndex TrainApproachingCrossingTile(const Train *v) * Checks for line end. Also, bars crossing at next tile if needed * * @param v vehicle we are checking + * @param reverse Set to false to not execute the vehicle reversing. This does not change any other logic. * @return true iff we did NOT have to reverse */ -static bool TrainCheckIfLineEnds(Train *v) +static bool TrainCheckIfLineEnds(Train *v, bool reverse) { /* First, handle broken down train */ @@ -3531,11 +3582,11 @@ static bool TrainCheckIfLineEnds(Train *v) /* no suitable trackbits at all || unusable rail (wrong type or owner) */ if (bits == TRACK_BIT_NONE || !CheckCompatibleRail(v, tile)) { - return TrainApproachingLineEnd(v, false); + return TrainApproachingLineEnd(v, false, reverse); } /* approaching red signal */ - if ((trackdirbits & red_signals) != 0) return TrainApproachingLineEnd(v, true); + if ((trackdirbits & red_signals) != 0) return TrainApproachingLineEnd(v, true, reverse); /* approaching a rail/road crossing? then make it red */ if (IsLevelCrossingTile(tile)) MaybeBarCrossingWithSound(tile); diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 69c40975b..4d16fc3a7 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -962,7 +962,7 @@ static void DoDrawVehicle(const Vehicle *v) } AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs, - v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed); + v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs); } /** @@ -2213,6 +2213,11 @@ void Vehicle::ShowVisualEffect() const continue; } + /* The effect offset is relative to a point 4 units behind the vehicle's + * front (which is the center of an 8/8 vehicle). Shorter vehicles need a + * correction factor. */ + if (v->type == VEH_TRAIN) effect_offset += (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2; + int x = _vehicle_smoke_pos[v->direction] * effect_offset; int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset; diff --git a/src/vehicle_base.h b/src/vehicle_base.h index e7c8fd33e..5ac7bd540 100644 --- a/src/vehicle_base.h +++ b/src/vehicle_base.h @@ -194,6 +194,8 @@ public: byte x_extent; ///< x-extent of vehicle bounding box byte y_extent; ///< y-extent of vehicle bounding box byte z_extent; ///< z-extent of vehicle bounding box + int8 x_bb_offs; ///< x offset of vehicle bounding box + int8 y_bb_offs; ///< y offset of vehicle bounding box int8 x_offs; ///< x offset for vehicle sprite int8 y_offs; ///< y offset for vehicle sprite EngineID engine_type; ///< The type of engine used for this vehicle. |