diff --git a/src/ground_vehicle.cpp b/src/ground_vehicle.cpp index e6bc103c46..9e01b42e6a 100644 --- a/src/ground_vehicle.cpp +++ b/src/ground_vehicle.cpp @@ -13,6 +13,8 @@ #include "train.h" #include "roadveh.h" #include "depot_map.h" +#include "tunnel_base.h" +#include "slope_type.h" #include "safeguards.h" @@ -280,6 +282,66 @@ bool GroundVehicle::IsChainInDepot() const return true; } +/** + * Updates vehicle's Z inclination inside a wormhole, where applicable. + */ +template +void GroundVehicle::UpdateZPositionInWormhole() +{ + if (!IsTunnel(this->tile)) return; + + const Tunnel *t = Tunnel::GetByTile(this->tile); + if (!t->is_chunnel) return; + + TileIndex pos_tile = TileVirtXY(this->x_pos, this->y_pos); + + ClrBit(this->gv_flags, GVF_GOINGUP_BIT); + ClrBit(this->gv_flags, GVF_GOINGDOWN_BIT); + + if (pos_tile == t->tile_n || pos_tile == t->tile_s) { + this->z_pos = 0; + return; + } + + int north_coord, south_coord, pos_coord; + bool going_north; + Slope slope_north; + if (t->tile_s - t->tile_n > MapMaxX()) { + // tunnel extends along Y axis (DIAGDIR_SE from north end), has same X values + north_coord = TileY(t->tile_n); + south_coord = TileY(t->tile_s); + pos_coord = TileY(pos_tile); + going_north = (this->direction == DIR_NW); + slope_north = SLOPE_NW; + } else { + // tunnel extends along X axis (DIAGDIR_SW from north end), has same Y values + north_coord = TileX(t->tile_n); + south_coord = TileX(t->tile_s); + pos_coord = TileX(pos_tile); + going_north = (this->direction == DIR_NE); + slope_north = SLOPE_NE; + } + + Slope slope = SLOPE_FLAT; + + int delta; + if ((delta = pos_coord - north_coord) <= 3) { + this->z_pos = TILE_HEIGHT * (delta == 3 ? -2 : -1); + if (delta != 2) { + slope = slope_north; + SetBit(this->gv_flags, going_north ? GVF_GOINGUP_BIT : GVF_GOINGDOWN_BIT); + } + } else if ((delta = south_coord - pos_coord) <= 3) { + this->z_pos = TILE_HEIGHT * (delta == 3 ? -2 : -1); + if (delta != 2) { + slope = SLOPE_ELEVATED ^ slope_north; + SetBit(this->gv_flags, going_north ? GVF_GOINGDOWN_BIT : GVF_GOINGUP_BIT); + } + } + + if (slope != SLOPE_FLAT) this->z_pos += GetPartialPixelZ(this->x_pos & 0xF, this->y_pos & 0xF, slope); +} + /* Instantiation for Train */ template struct GroundVehicle; /* Instantiation for RoadVehicle */ diff --git a/src/ground_vehicle.hpp b/src/ground_vehicle.hpp index 67676fb919..5585fe7760 100644 --- a/src/ground_vehicle.hpp +++ b/src/ground_vehicle.hpp @@ -228,17 +228,21 @@ struct GroundVehicle : public SpecializedVehicle { assert(this->z_pos == GetSlopePixelZ(this->x_pos, this->y_pos)); } + void UpdateZPositionInWormhole(); + /** * Checks if the vehicle is in a slope and sets the required flags in that case. * @param new_tile True if the vehicle reached a new tile. * @param update_delta Indicates to also update the delta. * @return Old height of the vehicle. */ - inline int UpdateInclination(bool new_tile, bool update_delta) + inline int UpdateInclination(bool new_tile, bool update_delta, bool in_wormhole = false) { int old_z = this->z_pos; - if (new_tile) { + if (in_wormhole) { + this->UpdateZPositionInWormhole(); + } else if (new_tile) { this->UpdateZPositionAndInclination(); } else { this->UpdateZPosition(); diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp index 91a14d5df0..8c8a8b42da 100644 --- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -1184,6 +1184,7 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev) v->x_pos = gp.x; v->y_pos = gp.y; v->UpdatePosition(); + RoadZPosAffectSpeed(v, v->UpdateInclination(false, false, true)); if (v->IsDrawn()) v->Vehicle::UpdateViewport(true); return true; } @@ -1555,7 +1556,7 @@ again: v->x_pos = x; v->y_pos = y; v->UpdatePosition(); - RoadZPosAffectSpeed(v, v->UpdateInclination(false, true)); + RoadZPosAffectSpeed(v, v->UpdateInclination(false, true, v->state == RVSB_WORMHOLE)); return true; } diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 2743e17e45..679efbb188 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -1685,6 +1685,7 @@ static void UpdateStatusAfterSwap(Train *v) } v->UpdatePosition(); + if (v->track == TRACK_BIT_WORMHOLE) v->UpdateInclination(false, false, true); v->UpdateViewport(true, true); } @@ -3903,6 +3904,15 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse) v->x_pos = gp.x; v->y_pos = gp.y; v->UpdatePosition(); + if (v->track == TRACK_BIT_WORMHOLE) { + /* update the Z position of the vehicle */ + int old_z = v->UpdateInclination(false, false, true); + + if (prev == NULL) { + /* This is the first vehicle in the train */ + AffectSpeedByZChange(v, old_z); + } + } if (v->IsDrawn()) v->Vehicle::UpdateViewport(true); continue; } @@ -3919,7 +3929,7 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse) } /* update the Z position of the vehicle */ - int old_z = v->UpdateInclination(gp.new_tile != gp.old_tile, false); + int old_z = v->UpdateInclination(gp.new_tile != gp.old_tile, false, v->track == TRACK_BIT_WORMHOLE); if (prev == NULL) { /* This is the first vehicle in the train */