mirror of https://github.com/OpenTTD/OpenTTD
Change: Check whether to stay in an extended rail depot or place the train and reverse if appropriate.
parent
0619cb633a
commit
dfe6be59f4
|
@ -817,7 +817,7 @@ struct DepotWindow : Window {
|
|||
case WID_D_STOP_ALL:
|
||||
case WID_D_START_ALL: {
|
||||
VehicleListIdentifier vli(VL_DEPOT_LIST, this->type, this->owner);
|
||||
Command<CMD_MASS_START_STOP>::Post(tile, widget == WID_D_START_ALL, false, vli);
|
||||
Command<CMD_MASS_START_STOP>::Post(STR_ERROR_CAN_T_START_STOP_VEHICLES, tile, widget == WID_D_START_ALL, false, vli);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -5160,6 +5160,7 @@ STR_ERROR_DEPOT_TYPE_NOT_AVAILABLE :{WHITE}... depo
|
|||
STR_ERROR_DEPOT_EXTENDING_PLATFORMS :{WHITE}Extending already reserved depot platforms
|
||||
STR_ERROR_DEPOT_EXTENDED_RAIL_DEPOT_IS_NOT_FREE :{WHITE}Extended rail depot has a reserved tile and can't be converted
|
||||
|
||||
STR_ERROR_CAN_T_START_STOP_VEHICLES :{WHITE}Can't start at least one vehicle in the depot...
|
||||
STR_ERROR_CAN_T_RENAME_DEPOT :{WHITE}Can't rename depot...
|
||||
|
||||
STR_ERROR_TRAIN_MUST_BE_STOPPED_INSIDE_DEPOT :{WHITE}... must be stopped inside a depot
|
||||
|
|
|
@ -882,6 +882,10 @@ CommandCost CmdBuildRailVehicle(DoCommandFlag flags, TileIndex tile, const Engin
|
|||
UpdateTrainGroupID(v);
|
||||
|
||||
CheckConsistencyOfArticulatedVehicle(v);
|
||||
|
||||
TrainPlacement train_placement;
|
||||
train_placement.LiftTrain(v, flags);
|
||||
train_placement.PlaceTrain(v, flags);
|
||||
}
|
||||
|
||||
return CommandCost();
|
||||
|
@ -1050,82 +1054,6 @@ static CommandCost CheckNewTrain(Train *original_dst, Train *dst, Train *origina
|
|||
return_cmd_error(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a train can be placed in a depot tile.
|
||||
* @param train The train.
|
||||
* @param tile The tile to check whether it is possible to place the train.
|
||||
* @return whether it found a depot tile in which to place the train.
|
||||
*/
|
||||
bool CheckPlacement(const Train *train, TileIndex tile)
|
||||
{
|
||||
assert(train != nullptr);
|
||||
assert(IsRailDepotTile(tile));
|
||||
|
||||
RailType rt = GetRailType(tile);
|
||||
for (const Train *t = train; t != nullptr; t = t->Next()) {
|
||||
RailType rail_type = Engine::Get(t->engine_type)->u.rail.railtype;
|
||||
if (!IsCompatibleRail(rail_type, rt)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a valid tile before placing a train in the depot.
|
||||
* @param t The train to place in a rail depot tile.
|
||||
* @return a compatible tile, if any, preferabily the one the first vehicle is or INVALID_TILE if none found.
|
||||
*/
|
||||
TileIndex LookForTileInDepot(const Train *train)
|
||||
{
|
||||
assert(train != nullptr);
|
||||
assert(IsRailDepotTile(train->tile));
|
||||
TileIndex best_tile = INVALID_TILE;
|
||||
|
||||
/* First candidate is the original position of the train. */
|
||||
if (CheckPlacement(train, train->tile)) {
|
||||
if (HasPowerOnRail(train->railtype, GetRailType(train->tile))) return train->tile;
|
||||
best_tile = train->tile;
|
||||
}
|
||||
|
||||
/* Check all depot tiles. */
|
||||
Depot *depot = Depot::GetByTile(train->tile);
|
||||
for (std::vector<TileIndex>::iterator it = depot->depot_tiles.begin(); it != depot->depot_tiles.end(); ++it) {
|
||||
if (CheckPlacement(train, *it)) {
|
||||
if (HasPowerOnRail(train->railtype, GetRailType(*it))) return *it;
|
||||
if (best_tile == INVALID_TILE) best_tile = *it;
|
||||
}
|
||||
}
|
||||
|
||||
return best_tile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an appropriate depot tile for a train and place
|
||||
* all the vehicle chain in the same depot tile.
|
||||
* @param train The train to place.
|
||||
*/
|
||||
void PlaceOnRailDepot(Train *train)
|
||||
{
|
||||
assert(train->First() == train);
|
||||
|
||||
TileIndex depot_tile = LookForTileInDepot(train);
|
||||
assert(depot_tile != INVALID_TILE);
|
||||
|
||||
DiagDirection diag_dir = GetRailDepotDirection(depot_tile);
|
||||
int x = TileX(depot_tile) * TILE_SIZE + _vehicle_initial_x_fract[diag_dir];
|
||||
int y = TileY(depot_tile) * TILE_SIZE + _vehicle_initial_y_fract[diag_dir];
|
||||
for (Train *t = train; t != nullptr; t = t->Next()) {
|
||||
t->tile = depot_tile;
|
||||
t->direction = DiagDirToDir(diag_dir);
|
||||
t->vehstatus |= VS_HIDDEN;
|
||||
t->track = TRACK_BIT_DEPOT;
|
||||
t->x_pos = x;
|
||||
t->y_pos = y;
|
||||
t->z_pos = GetSlopePixelZ(x, y);
|
||||
t->UpdatePosition();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the train parts can be attached.
|
||||
* @param t the train to check
|
||||
|
@ -1136,7 +1064,9 @@ static CommandCost CheckTrainAttachment(Train *t)
|
|||
/* No multi-part train, no need to check. */
|
||||
if (t == nullptr || t->Next() == nullptr) return CommandCost();
|
||||
|
||||
if (LookForTileInDepot(t) == INVALID_TILE) return_cmd_error(STR_ERROR_INCOMPATIBLE_RAILTYPES_WITH_DEPOT);
|
||||
TrainPlacement tp;
|
||||
tp.LookForPlaceInDepot(t, false);
|
||||
if (tp.info == PI_FAILED_PLATFORM_TYPE) return_cmd_error(STR_ERROR_INCOMPATIBLE_RAILTYPES_WITH_DEPOT);
|
||||
|
||||
/* The maximum length for a train. For each part we decrease this by one
|
||||
* and if the result is negative the train is simply too long. */
|
||||
|
@ -1414,6 +1344,13 @@ CommandCost CmdMoveRailVehicle(DoCommandFlag flags, VehicleID src_veh, VehicleID
|
|||
bool original_src_head_front_engine = original_src_head->IsFrontEngine();
|
||||
bool original_dst_head_front_engine = original_dst_head != nullptr && original_dst_head->IsFrontEngine();
|
||||
|
||||
TrainPlacement train_placement_src;
|
||||
TrainPlacement train_placement_dst;
|
||||
train_placement_src.LiftTrain(src_head, flags);
|
||||
train_placement_dst.LiftTrain(dst_head, flags);
|
||||
|
||||
assert(src_head != nullptr);
|
||||
|
||||
/* (Re)arrange the trains in the wanted arrangement. */
|
||||
ArrangeTrains(&dst_head, dst, &src_head, src, move_chain);
|
||||
|
||||
|
@ -1426,6 +1363,8 @@ CommandCost CmdMoveRailVehicle(DoCommandFlag flags, VehicleID src_veh, VehicleID
|
|||
/* Restore the train we had. */
|
||||
RestoreTrainBackup(original_src);
|
||||
RestoreTrainBackup(original_dst);
|
||||
train_placement_src.PlaceTrain(original_src_head, flags & ~DC_EXEC);
|
||||
if (src_head != dst_head) train_placement_dst.PlaceTrain(original_dst_head, flags & ~DC_EXEC);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
@ -1503,15 +1442,14 @@ CommandCost CmdMoveRailVehicle(DoCommandFlag flags, VehicleID src_veh, VehicleID
|
|||
CheckCargoCapacity(dst_head);
|
||||
}
|
||||
|
||||
if (src_head != nullptr) {
|
||||
PlaceOnRailDepot(src_head->First());
|
||||
src_head->First()->MarkDirty();
|
||||
}
|
||||
if (src_head != nullptr) src_head->First()->MarkDirty();
|
||||
if (dst_head != nullptr) dst_head->First()->MarkDirty();
|
||||
|
||||
if (dst_head != nullptr) {
|
||||
PlaceOnRailDepot(dst_head->First());
|
||||
dst_head->First()->MarkDirty();
|
||||
}
|
||||
bool reverse_emplacement_order = !train_placement_src.placed && train_placement_dst.placed;
|
||||
|
||||
if (!reverse_emplacement_order) train_placement_src.PlaceTrain(src_head, flags);
|
||||
if (src_head != dst_head) train_placement_dst.PlaceTrain(dst_head, flags);
|
||||
if (reverse_emplacement_order) train_placement_src.PlaceTrain(src_head, flags);
|
||||
|
||||
/* We are undoubtedly changing something in the depot and train list. */
|
||||
InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(src->tile));
|
||||
|
@ -1520,6 +1458,8 @@ CommandCost CmdMoveRailVehicle(DoCommandFlag flags, VehicleID src_veh, VehicleID
|
|||
/* We don't want to execute what we're just tried. */
|
||||
RestoreTrainBackup(original_src);
|
||||
RestoreTrainBackup(original_dst);
|
||||
train_placement_src.PlaceTrain(original_src_head, flags);
|
||||
if (src_head != dst_head) train_placement_dst.PlaceTrain(original_dst_head, flags);
|
||||
}
|
||||
|
||||
return CommandCost();
|
||||
|
@ -1544,6 +1484,9 @@ CommandCost CmdSellRailWagon(DoCommandFlag flags, Vehicle *t, bool sell_chain, b
|
|||
|
||||
if (v->IsRearDualheaded()) return_cmd_error(STR_ERROR_REAR_ENGINE_FOLLOW_FRONT);
|
||||
|
||||
TrainPlacement train_placement;
|
||||
train_placement.LiftTrain(first, flags);
|
||||
|
||||
/* First make a backup of the order of the train. That way we can do
|
||||
* whatever we want with the order and later on easily revert. */
|
||||
TrainList original;
|
||||
|
@ -1561,12 +1504,14 @@ CommandCost CmdSellRailWagon(DoCommandFlag flags, Vehicle *t, bool sell_chain, b
|
|||
if (ret.Failed()) {
|
||||
/* Restore the train we had. */
|
||||
RestoreTrainBackup(original);
|
||||
train_placement.PlaceTrain(first, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (first->orders == nullptr && !OrderList::CanAllocateItem()) {
|
||||
/* Restore the train we had. */
|
||||
RestoreTrainBackup(original);
|
||||
train_placement.PlaceTrain(first, flags);
|
||||
return_cmd_error(STR_ERROR_NO_MORE_SPACE_FOR_ORDERS);
|
||||
}
|
||||
|
||||
|
@ -1596,6 +1541,7 @@ CommandCost CmdSellRailWagon(DoCommandFlag flags, Vehicle *t, bool sell_chain, b
|
|||
|
||||
/* We need to update the information about the train. */
|
||||
NormaliseTrainHead(new_head);
|
||||
train_placement.PlaceTrain(new_head, flags);
|
||||
|
||||
/* We are undoubtedly changing something in the depot and train list. */
|
||||
InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile));
|
||||
|
@ -1604,8 +1550,9 @@ CommandCost CmdSellRailWagon(DoCommandFlag flags, Vehicle *t, bool sell_chain, b
|
|||
/* Actually delete the sold 'goods' */
|
||||
delete sell_head;
|
||||
} else {
|
||||
/* We don't want to execute what we're just tried. */
|
||||
/* We don't want to execute what we have just tried. */
|
||||
RestoreTrainBackup(original);
|
||||
train_placement.PlaceTrain(first, flags);
|
||||
}
|
||||
|
||||
return cost;
|
||||
|
@ -2116,7 +2063,11 @@ static bool IsWholeTrainInsideDepot(const Train *v)
|
|||
void ReverseTrainDirection(Train *v)
|
||||
{
|
||||
if (IsRailDepotTile(v->tile)) {
|
||||
if (IsExtendedDepot(v->tile)) {
|
||||
if ((v->track & TRACK_BIT_DEPOT) != 0 && (v->track & ~TRACK_BIT_DEPOT) != 0) return;
|
||||
} else {
|
||||
if (IsWholeTrainInsideDepot(v)) return;
|
||||
}
|
||||
InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile));
|
||||
}
|
||||
|
||||
|
@ -2230,7 +2181,14 @@ CommandCost CmdReverseTrainDirection(DoCommandFlag flags, VehicleID veh_id, bool
|
|||
ToggleBit(v->flags, VRF_REVERSE_DIRECTION);
|
||||
|
||||
front->ConsistChanged(CCF_ARRANGE);
|
||||
if (IsRailDepotTile(front->tile)) SetWindowDirty(WC_VEHICLE_DEPOT, GetDepotIndex(front->tile));
|
||||
if (IsRailDepotTile(front->tile)) {
|
||||
if (IsExtendedDepot(front->tile)) {
|
||||
TrainPlacement tp;
|
||||
tp.LiftTrain(front, flags);
|
||||
tp.PlaceTrain(front, flags & ~DC_EXEC);
|
||||
}
|
||||
SetWindowDirty(WC_VEHICLE_DEPOT, GetDepotIndex(front->tile));
|
||||
}
|
||||
SetWindowDirty(WC_VEHICLE_DETAILS, front->index);
|
||||
SetWindowDirty(WC_VEHICLE_VIEW, front->index);
|
||||
SetWindowClassesDirty(WC_TRAINS_LIST);
|
||||
|
@ -2240,6 +2198,9 @@ CommandCost CmdReverseTrainDirection(DoCommandFlag flags, VehicleID veh_id, bool
|
|||
if (!v->IsPrimaryVehicle()) return CMD_ERROR;
|
||||
if ((v->vehstatus & VS_CRASHED) || v->breakdown_ctr != 0) return CMD_ERROR;
|
||||
|
||||
/* Do not reverse while servicing. */
|
||||
if (IsExtendedRailDepotTile(v->tile) && (v->track & TRACK_BIT_DEPOT) != 0 && (v->track & ~TRACK_BIT_DEPOT) != 0) return CMD_ERROR;
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
/* Properly leave the station if we are loading and won't be loading anymore */
|
||||
if (v->current_order.IsType(OT_LOADING)) {
|
||||
|
@ -2468,15 +2429,62 @@ static bool CheckTrainStayInDepot(Train *v)
|
|||
|
||||
DepotID depot_id = GetDepotIndex(v->tile);
|
||||
if (IsExtendedRailDepot(v->tile)) {
|
||||
for (Train *u = v; u != nullptr; u = u->Next()) u->track &= ~TRACK_BIT_DEPOT;
|
||||
v->cur_speed = 0;
|
||||
/* If not placed, try it. If not possible, exit. */
|
||||
if (CheckIfTrainNeedsPlacement(v)) {
|
||||
/* If stuck, wait a little bit, so we can avoid
|
||||
* trying placing it as much as we can. */
|
||||
bool already_stuck = false;
|
||||
bool send_message = false;
|
||||
if (HasBit(v->flags, VRF_TRAIN_STUCK)) {
|
||||
already_stuck = true;
|
||||
v->wait_counter++;
|
||||
if (v->wait_counter % (1 << 7) != 0) {
|
||||
return true;
|
||||
} else if (v->wait_counter % (1 << 11) == 0) {
|
||||
send_message = true;
|
||||
}
|
||||
ClrBit(v->flags, VRF_TRAIN_STUCK);
|
||||
}
|
||||
|
||||
v->UpdatePosition();
|
||||
v->UpdateViewport(true, true);
|
||||
TrainPlacement train_placement;
|
||||
train_placement.LiftTrain(v, DC_EXEC);
|
||||
train_placement.LookForPlaceInDepot(v, true);
|
||||
if (train_placement.info < PI_FAILED_END) {
|
||||
if (send_message) {
|
||||
ClrBit(v->flags, VRF_TRAIN_STUCK);
|
||||
/* Show message to player. */
|
||||
if (_settings_client.gui.lost_vehicle_warn && v->owner == _local_company) {
|
||||
SetDParam(0, v->index);
|
||||
AddVehicleAdviceNewsItem(STR_ADVICE_PLATFORM_TYPE + train_placement.info - PI_ERROR_BEGIN, v->index);
|
||||
}
|
||||
}
|
||||
if (already_stuck) {
|
||||
SetBit(v->flags, VRF_TRAIN_STUCK);
|
||||
} else {
|
||||
MarkTrainAsStuck(v);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
VehicleServiceInExtendedDepot(v);
|
||||
train_placement.PlaceTrain(v, DC_EXEC);
|
||||
if (!IsExtendedDepot(v->tile)) return true;
|
||||
}
|
||||
} else {
|
||||
VehicleServiceInExtendedDepot(v);
|
||||
}
|
||||
|
||||
for (Train *u = v; u != nullptr; u = u->Next()) u->track &= ~TRACK_BIT_DEPOT;
|
||||
|
||||
v->cur_speed = 0;
|
||||
v->UpdateAcceleration();
|
||||
ProcessOrders(v);
|
||||
if (CheckReverseTrain(v)) ReverseTrainDirection(v);
|
||||
InvalidateWindowData(WC_VEHICLE_DEPOT, depot_id);
|
||||
|
||||
/* Check whether it is safe to exit the depot. */
|
||||
if (UpdateSignalsOnSegment(v->tile, VehicleExitDir(v->direction, v->track), v->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) {
|
||||
if (!TryPathReserve(v, true, true)) return true;
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
for (const Train *u = v; u != nullptr; u = u->Next()) {
|
||||
|
|
|
@ -38,6 +38,8 @@
|
|||
#include "train_cmd.h"
|
||||
#include "ship_cmd.h"
|
||||
#include "depot_base.h"
|
||||
#include "train_placement.h"
|
||||
#include "strings_func.h"
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
|
@ -523,6 +525,11 @@ std::tuple<CommandCost, uint, uint16_t, CargoArray> CmdRefitVehicle(DoCommandFla
|
|||
/* For ships and aircraft there is always only one. */
|
||||
only_this |= front->type == VEH_SHIP || front->type == VEH_AIRCRAFT;
|
||||
|
||||
/* If it is a train on a depot, lift it. New length of the vehicle won't be checked. */
|
||||
Train *train = (v->type == VEH_TRAIN && IsDepotTile(front->tile)) ? Train::From(front) : nullptr;
|
||||
TrainPlacement train_placement;
|
||||
train_placement.LiftTrain(train, flags);
|
||||
|
||||
auto [cost, refit_capacity, mail_capacity, cargo_capacities] = RefitVehicle(v, only_this, num_vehicles, new_cid, new_subtype, flags, auto_refit);
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
|
@ -560,6 +567,9 @@ std::tuple<CommandCost, uint, uint16_t, CargoArray> CmdRefitVehicle(DoCommandFla
|
|||
v->InvalidateNewGRFCacheOfChain();
|
||||
}
|
||||
|
||||
/* If it is a train on an extended depot, try placing it. */
|
||||
train_placement.PlaceTrain(train, flags);
|
||||
|
||||
return { cost, refit_capacity, mail_capacity, cargo_capacities };
|
||||
}
|
||||
|
||||
|
@ -584,9 +594,20 @@ CommandCost CmdStartStopVehicle(DoCommandFlag flags, VehicleID veh_id, bool eval
|
|||
if (v->vehstatus & VS_CRASHED) return_cmd_error(STR_ERROR_VEHICLE_IS_DESTROYED);
|
||||
|
||||
switch (v->type) {
|
||||
case VEH_TRAIN:
|
||||
if ((v->vehstatus & VS_STOPPED) && Train::From(v)->gcache.cached_power == 0) return_cmd_error(STR_ERROR_TRAIN_START_NO_POWER);
|
||||
case VEH_TRAIN: {
|
||||
Train *t = Train::From(v);
|
||||
if ((v->vehstatus & VS_STOPPED) && t->gcache.cached_power == 0) return_cmd_error(STR_ERROR_TRAIN_START_NO_POWER);
|
||||
|
||||
/* Train cannot leave until changing the depot. Stop the train and send a message. */
|
||||
if (!(flags & DC_AUTOREPLACE) && v->IsStoppedInDepot() && CheckIfTrainNeedsPlacement(t)) {
|
||||
TrainPlacement train_placement;
|
||||
if (!train_placement.CanFindAppropriatePlatform(t, (flags & DC_EXEC) != 0)) {
|
||||
SetDParam(0, v->index);
|
||||
return_cmd_error(STR_ERROR_CAN_T_START_PLATFORM_TYPE + train_placement.info - PI_FAILED_PLATFORM_TYPE);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case VEH_SHIP:
|
||||
case VEH_ROAD:
|
||||
|
@ -638,6 +659,11 @@ CommandCost CmdStartStopVehicle(DoCommandFlag flags, VehicleID veh_id, bool eval
|
|||
/* Unbunching data is no longer valid. */
|
||||
v->ResetDepotUnbunching();
|
||||
|
||||
if (v->type == VEH_TRAIN && v->IsInDepot() && IsExtendedDepotTile(v->tile)) {
|
||||
if ((v->vehstatus & VS_STOPPED) != 0) FreeTrainTrackReservation(Train::From(v));
|
||||
UpdateExtendedDepotReservation(v, true);
|
||||
}
|
||||
|
||||
v->MarkDirty();
|
||||
SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
|
||||
if (IsDepotTile(v->tile)) SetWindowDirty(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile));
|
||||
|
@ -677,7 +703,14 @@ CommandCost CmdMassStartStopVehicle(DoCommandFlag flags, TileIndex tile, bool do
|
|||
if (!vehicle_list_window && !v->IsChainInDepot()) continue;
|
||||
|
||||
/* Just try and don't care if some vehicle's can't be stopped. */
|
||||
Command<CMD_START_STOP_VEHICLE>::Do(flags, v->index, false);
|
||||
CommandCost ret = Command<CMD_START_STOP_VEHICLE>::Do(flags, v->index, false);
|
||||
if (ret.Failed()) {
|
||||
if (ret.GetErrorMessage() == STR_ERROR_CAN_T_START_PLATFORM_TYPE ||
|
||||
ret.GetErrorMessage() == STR_ERROR_CAN_T_START_PLATFORM_LONG) {
|
||||
SetDParam(0, v->index);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return CommandCost();
|
||||
|
|
Loading…
Reference in New Issue