summaryrefslogtreecommitdiff
path: root/src/train_cmd.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/train_cmd.cpp')
-rw-r--r--src/train_cmd.cpp241
1 files changed, 236 insertions, 5 deletions
diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp
index a01612511..280643b3c 100644
--- a/src/train_cmd.cpp
+++ b/src/train_cmd.cpp
@@ -34,6 +34,7 @@
#include "newgrf.h"
#include "order_backup.h"
#include "zoom_func.h"
+#include "cargodest_func.h"
#include "table/strings.h"
#include "table/train_cmd.h"
@@ -156,6 +157,8 @@ void Train::ConsistChanged(bool same_length)
u->InvalidateNewGRFCache();
}
+ uint32 cargo_mask = 0;
+
for (Train *u = this; u != NULL; u = u->Next()) {
const Engine *e_u = u->GetEngine();
const RailVehicleInfo *rvi_u = &e_u->u.rail;
@@ -200,7 +203,9 @@ void Train::ConsistChanged(bool same_length)
}
}
+ /* Store carried cargo. */
u->cargo_cap = e_u->DetermineCapacity(u);
+ if (u->cargo_type != INVALID_CARGO && u->cargo_cap > 0) SetBit(cargo_mask, u->cargo_type);
u->vcache.cached_cargo_age_period = GetVehicleProperty(u, PROP_TRAIN_CARGO_AGE_PERIOD, e_u->info.cargo_age_period);
/* check the vehicle length (callback) */
@@ -231,6 +236,7 @@ void Train::ConsistChanged(bool same_length)
}
/* store consist weight/max speed in cache */
+ this->vcache.cached_cargo_mask = cargo_mask;
this->vcache.cached_max_speed = max_speed;
this->tcache.cached_tilt = train_can_tilt;
this->tcache.cached_max_curve_speed = this->GetCurveSpeedLimit();
@@ -1110,6 +1116,8 @@ static void NormaliseTrainHead(Train *head)
/* Not a front engine, i.e. a free wagon chain. No need to do more. */
if (!head->IsFrontEngine()) return;
+ PrefillRouteLinks(head);
+
/* Update the refit button and window */
InvalidateWindowData(WC_VEHICLE_REFIT, head->index, VIWD_CONSIST_CHANGED);
SetWindowWidgetDirty(WC_VEHICLE_VIEW, head->index, WID_VV_REFIT);
@@ -1296,6 +1304,9 @@ CommandCost CmdMoveRailVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, u
CheckCargoCapacity(dst_head);
}
+ /* Pre-fill route links after adding a vehicle. */
+ if (dst_head != NULL && dst_head->IsFrontEngine()) PrefillRouteLinks(dst_head);
+
/* We are undoubtedly changing something in the depot and train list. */
InvalidateWindowData(WC_VEHICLE_DEPOT, src->tile);
InvalidateWindowClassesData(WC_TRAINS_LIST, 0);
@@ -1828,6 +1839,17 @@ void ReverseTrainDirection(Train *v)
return;
}
+ /* We are inside tunnel/bidge with signals, reversing will close the entrance. */
+ if (HasWormholeSignals(v->tile)) {
+ /* Flip signal on tunnel entrance tile red. */
+ SetBitTunnelBridgeExit(v->tile);
+ MarkTileDirtyByTile(v->tile);
+ /* Clear counters. */
+ v->wait_counter = 0;
+ v->load_unload_ticks = 0;
+ return;
+ }
+
/* TrainExitDir does not always produce the desired dir for depots and
* tunnels/bridges that is needed for UpdateSignalsOnSegment. */
DiagDirection dir = TrainExitDir(v->direction, v->track);
@@ -2164,6 +2186,42 @@ static bool CheckTrainStayInDepot(Train *v)
return false;
}
+static void HandleLastTunnelBridgeSignals(TileIndex tile, TileIndex end, DiagDirection dir, bool free)
+{
+ if (IsBridge(end) && _m[end].m2 > 0){
+ /* Clearing last bridge signal. */
+ uint16 m = _m[end].m2;
+ byte i = 15;
+ while((m & 0x8000) == 0 && --i > 0) m <<= 1;
+ ClrBit(_m[end].m2, i);
+
+ uint x = TileX(end)* TILE_SIZE;
+ uint y = TileY(end)* TILE_SIZE;
+ uint distance = (TILE_SIZE * _settings_client.gui.simulated_wormhole_signals) * ++i;
+ switch (dir) {
+ default: NOT_REACHED();
+ case DIAGDIR_NE: MarkTileDirtyByTile(TileVirtXY(x - distance, y)); break;
+ case DIAGDIR_SE: MarkTileDirtyByTile(TileVirtXY(x, y + distance)); break;
+ case DIAGDIR_SW: MarkTileDirtyByTile(TileVirtXY(x + distance, y)); break;
+ case DIAGDIR_NW: MarkTileDirtyByTile(TileVirtXY(x, y - distance)); break;
+ }
+ MarkTileDirtyByTile(tile);
+ }
+ if (free) {
+ /* Open up the wormhole and clear m2. */
+ _m[tile].m2 = 0;
+ _m[end].m2 = 0;
+
+ if (IsTunnelBridgeWithSignRed(end)) {
+ ClrBitTunnelBridgeExit(end);
+ if (!_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(end);
+ } else if (IsTunnelBridgeWithSignRed(tile)) {
+ ClrBitTunnelBridgeExit(tile);
+ if (!_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(tile);
+ }
+ }
+}
+
/**
* Clear the reservation of \a tile that was just left by a wagon on \a track_dir.
* @param v %Train owning the reservation.
@@ -2179,7 +2237,8 @@ static void ClearPathReservation(const Train *v, TileIndex tile, Trackdir track_
if (GetTunnelBridgeDirection(tile) == ReverseDiagDir(dir)) {
TileIndex end = GetOtherTunnelBridgeEnd(tile);
- if (TunnelBridgeIsFree(tile, end, v).Succeeded()) {
+ bool free = TunnelBridgeIsFree(tile, end, v).Succeeded();
+ if (free) {
/* Free the reservation only if no other train is on the tiles. */
SetTunnelBridgeReservation(tile, false);
SetTunnelBridgeReservation(end, false);
@@ -2189,6 +2248,7 @@ static void ClearPathReservation(const Train *v, TileIndex tile, Trackdir track_
MarkTileDirtyByTile(end);
}
}
+ if (HasWormholeSignals(tile)) HandleLastTunnelBridgeSignals(tile, end, dir, free);
}
} else if (IsRailStationTile(tile)) {
TileIndex new_tile = TileAddByDiagDir(tile, dir);
@@ -2789,8 +2849,6 @@ int Train::UpdateSpeed()
*/
static void TrainEnterStation(Train *v, StationID station)
{
- v->last_station_visited = station;
-
/* check if a train ever visited this station before */
Station *st = Station::Get(station);
if (!(st->had_vehicle_of_type & HVOT_TRAIN)) {
@@ -2809,7 +2867,7 @@ static void TrainEnterStation(Train *v, StationID station)
v->force_proceed = TFP_NONE;
SetWindowDirty(WC_VEHICLE_VIEW, v->index);
- v->BeginLoading();
+ v->BeginLoading(station);
TriggerStationRandomisation(st, v->tile, SRT_TRAIN_ARRIVES);
TriggerStationAnimation(st, v->tile, SAT_TRAIN_ARRIVES);
@@ -3055,6 +3113,101 @@ static Vehicle *CheckTrainAtSignal(Vehicle *v, void *data)
return t;
}
+
+
+/** Find train in front and keep distance between trains in tunnel/bridge. */
+static Vehicle *FindSpaceBetweenTrainsEnum(Vehicle *v, void *data)
+{
+ /* Don't look at wagons between front and back of train. */
+ if (v->type != VEH_TRAIN || (v->Previous() != NULL && v->Next() != NULL)) return NULL;
+
+ const Vehicle *u = (Vehicle*)data;
+ int32 a, b = 0;
+
+ switch (u->direction) {
+ default: NOT_REACHED();
+ case DIR_NE: a = u->x_pos; b = v->x_pos; break;
+ case DIR_SE: a = v->y_pos; b = u->y_pos; break;
+ case DIR_SW: a = v->x_pos; b = u->x_pos; break;
+ case DIR_NW: a = u->y_pos; b = v->y_pos; break;
+ }
+
+ if (a > b && a <= (b + (int)(Train::From(u)->wait_counter)) + (int)(TILE_SIZE)) return v;
+ return NULL;
+}
+
+static bool IsToCloseBehindTrain(Vehicle *v, TileIndex tile, bool check_endtile)
+{
+ Train *t = (Train *)v;
+
+ if (t->force_proceed != 0) return false;
+
+ if (HasVehicleOnPos(t->tile, v, &FindSpaceBetweenTrainsEnum)) {
+ /* Revert train if not going with tunnel direction. */
+ if (DirToDiagDir(t->direction) != GetTunnelBridgeDirection(t->tile)) {
+ v->cur_speed = 0;
+ ToggleBit(t->flags, VRF_REVERSING);
+ }
+ return true;
+ }
+ /* Cover blind spot at end of tunnel bridge. */
+ if (check_endtile){
+ if (HasVehicleOnPos(GetOtherTunnelBridgeEnd(t->tile), v, &FindSpaceBetweenTrainsEnum)) {
+ /* Revert train if not going with tunnel direction. */
+ if (DirToDiagDir(t->direction) != GetTunnelBridgeDirection(t->tile)) {
+ v->cur_speed = 0;
+ ToggleBit(t->flags, VRF_REVERSING);
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/** Simulate signals in tunnel - bridge. */
+static bool CheckTrainStayInWormHole(Train *t, TileIndex tile)
+{
+ if (t->force_proceed != 0) return false;
+
+ /* When not exit reverse train. */
+ if (!IsTunnelBridgeExit(tile)) {
+ t->cur_speed = 0;
+ ToggleBit(t->flags, VRF_REVERSING);
+ return true;
+ }
+ SigSegState seg_state = _settings_game.pf.reserve_paths ? SIGSEG_PBS : UpdateSignalsOnSegment(tile, INVALID_DIAGDIR, t->owner);
+ if (seg_state == SIGSEG_FULL || (seg_state == SIGSEG_PBS && !TryPathReserve(t))) {
+ t->cur_speed = 0;
+ return true;
+ }
+
+ return false;
+}
+
+static void HandleSignalBehindTrain(Train *v, uint signal_number)
+{
+ TileIndex tile;
+ switch (v->direction) {
+ default: NOT_REACHED();
+ case DIR_NE: tile = TileVirtXY(v->x_pos + (TILE_SIZE * _settings_client.gui.simulated_wormhole_signals), v->y_pos); break;
+ case DIR_SE: tile = TileVirtXY(v->x_pos, v->y_pos - (TILE_SIZE * _settings_client.gui.simulated_wormhole_signals) ); break;
+ case DIR_SW: tile = TileVirtXY(v->x_pos - (TILE_SIZE * _settings_client.gui.simulated_wormhole_signals), v->y_pos); break;
+ case DIR_NW: tile = TileVirtXY(v->x_pos, v->y_pos + (TILE_SIZE * _settings_client.gui.simulated_wormhole_signals)); break;
+ }
+
+ if(tile == v->tile) {
+ /* Flip signal on ramp. */
+ if (IsTunnelBridgeWithSignRed(tile)) {
+ ClrBitTunnelBridgeExit(tile);
+ MarkTileDirtyByTile(tile);
+ }
+ } else if (IsBridge(v->tile) && signal_number <= 16) {
+ ClrBit(_m[v->tile].m2, signal_number);
+ MarkTileDirtyByTile(tile);
+ }
+}
+
/**
* Move a vehicle chain one movement stop forwards.
* @param v First vehicle to move.
@@ -3154,7 +3307,9 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
/* Don't handle stuck trains here. */
if (HasBit(v->flags, VRF_TRAIN_STUCK)) return false;
-
+ /* this codepath seems to be run every 5 ticks, so increase counter twice every 20 ticks */
+ IncreaseStuckCounter(v->tile);
+ if (v->tick_counter % 4 == 0) IncreaseStuckCounter(v->tile);
if (!HasSignalOnTrackdir(gp.new_tile, ReverseTrackdir(i))) {
v->cur_speed = 0;
v->subspeed = 0;
@@ -3239,6 +3394,23 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
goto invalid_rail;
}
+ if (HasWormholeSignals(gp.new_tile)) {
+ /* If red signal stop. */
+ if (v->IsFrontEngine() && v->force_proceed == 0) {
+ if (IsTunnelBridgeWithSignRed(gp.new_tile)) {
+ v->cur_speed = 0;
+ return false;
+ }
+ if (IsTunnelBridgeExit(gp.new_tile)) {
+ v->cur_speed = 0;
+ goto invalid_rail;
+ }
+ /* Flip signal on tunnel entrance tile red. */
+ SetBitTunnelBridgeExit(gp.new_tile);
+ MarkTileDirtyByTile(gp.new_tile);
+ }
+ }
+
if (!HasBit(r, VETS_ENTERED_WORMHOLE)) {
Track track = FindFirstTrack(chosen_track);
Trackdir tdir = TrackDirectionToTrackdir(track, chosen_dir);
@@ -3291,6 +3463,64 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
}
}
} else {
+ /* Handle signal simulation on tunnel/bridge. */
+ TileIndex old_tile = TileVirtXY(v->x_pos, v->y_pos);
+ if (old_tile != gp.new_tile && HasWormholeSignals(v->tile) && (v->IsFrontEngine() || v->Next() == NULL)){
+ if (old_tile == v->tile) {
+ if (v->IsFrontEngine() && v->force_proceed == 0 && IsTunnelBridgeExit(v->tile)) goto invalid_rail;
+ /* Entered wormhole set counters. */
+ v->wait_counter = (TILE_SIZE * _settings_client.gui.simulated_wormhole_signals) - TILE_SIZE;
+ v->load_unload_ticks = 0;
+ }
+
+ uint distance = v->wait_counter;
+ bool leaving = false;
+ if (distance == 0) v->wait_counter = (TILE_SIZE * _settings_client.gui.simulated_wormhole_signals);
+
+ if (v->IsFrontEngine()) {
+ /* Check if track in front is free and see if we can leave wormhole. */
+ int z = GetSlopePixelZ(gp.x, gp.y) - v->z_pos;
+ if (IsTileType(gp.new_tile, MP_TUNNELBRIDGE) && !(abs(z) > 2)) {
+ if (CheckTrainStayInWormHole(v, gp.new_tile)) return false;
+ leaving = true;
+ } else {
+ if (IsToCloseBehindTrain(v, gp.new_tile, distance == 0)) {
+ if (distance == 0) v->wait_counter = 0;
+ v->cur_speed = 0;
+ return false;
+ }
+ /* flip signal in front to red on bridges*/
+ if (distance == 0 && v->load_unload_ticks <= 15 && IsBridge(v->tile)){
+ SetBit(_m[v->tile].m2, v->load_unload_ticks);
+ MarkTileDirtyByTile(gp.new_tile);
+ }
+ }
+ }
+ if (v->Next() == NULL) {
+ if (v->load_unload_ticks > 0 && v->load_unload_ticks <= 16 && distance == (TILE_SIZE * _settings_client.gui.simulated_wormhole_signals) - TILE_SIZE) HandleSignalBehindTrain(v, v->load_unload_ticks - 2);
+ if (old_tile == v->tile) {
+ /* We left ramp into wormhole. */
+ v->x_pos = gp.x;
+ v->y_pos = gp.y;
+ UpdateSignalsOnSegment(old_tile, INVALID_DIAGDIR, v->owner);
+ }
+ }
+ if (distance == 0) v->load_unload_ticks++;
+ v->wait_counter -= TILE_SIZE;
+
+ if (leaving) { // Reset counters.
+ v->force_proceed = 0;
+ v->wait_counter = 0;
+ v->load_unload_ticks = 0;
+ v->x_pos = gp.x;
+ v->y_pos = gp.y;
+ VehicleUpdatePosition(v);
+ VehicleUpdateViewport(v, false);
+ UpdateSignalsOnSegment(gp.new_tile, INVALID_DIAGDIR, v->owner);
+ continue;
+ }
+ }
+
if (IsTileType(gp.new_tile, MP_TUNNELBRIDGE) && HasBit(VehicleEnterTile(v, gp.new_tile, gp.x, gp.y), VETS_ENTERED_WORMHOLE)) {
/* Perform look-ahead on tunnel exit. */
if (v->IsFrontEngine()) {
@@ -3761,6 +3991,7 @@ static bool TrainLocoHandler(Train *v, bool mode)
/* Handle stuck trains. */
if (!mode && HasBit(v->flags, VRF_TRAIN_STUCK)) {
++v->wait_counter;
+ if (v->tick_counter % 4 == 0) IncreaseStuckCounter(v->tile);
/* Should we try reversing this tick if still stuck? */
bool turn_around = v->wait_counter % (_settings_game.pf.wait_for_pbs_path * DAY_TICKS) == 0 && _settings_game.pf.reverse_at_signals;