1
0
Fork 0

Add: Base files for placing trains in extended depots.

pull/8480/head
J0anJosep 2021-03-29 17:44:44 +02:00
parent 0e34a5afe2
commit 0619cb633a
6 changed files with 386 additions and 3 deletions

View File

@ -504,6 +504,8 @@ add_files(
train.h train.h
train_cmd.cpp train_cmd.cpp
train_cmd.h train_cmd.h
train_placement.cpp
train_placement.h
train_gui.cpp train_gui.cpp
transparency.h transparency.h
transparency_gui.cpp transparency_gui.cpp

View File

@ -5182,6 +5182,15 @@ STR_ERROR_UNABLE_TO_FIND_APPROPRIATE_DEPOT_TILE :{WHITE}Unable t
STR_ERROR_DEPOT_TOO_SPREAD_OUT :{WHITE}... depot too spread out STR_ERROR_DEPOT_TOO_SPREAD_OUT :{WHITE}... depot too spread out
STR_ERROR_DEPOT_WRONG_DEPOT_TYPE :Wrong depot type STR_ERROR_DEPOT_WRONG_DEPOT_TYPE :Wrong depot type
STR_ERROR_CAN_T_START_PLATFORM_TYPE :{WHITE}{VEHICLE} can't be started because there is no compatible platform in the depot for this type of train
STR_ERROR_CAN_T_START_PLATFORM_LONG :{WHITE}{VEHICLE} can't be started because compatible platforms are not long enough
STR_ERROR_DEPOT_FULL_DEPOT :There is no free depot compatible with this type of vehicle
###length 5
STR_ADVICE_PLATFORM_TYPE :{WHITE}{VEHICLE} can't leave depot because there is no compatible platform for this type of train
STR_ADVICE_PLATFORM_LONG :{WHITE}{VEHICLE} can't leave depot because compatible platforms are not long enough
STR_ADVICE_VEHICLE_HAS_NO_POWER :{WHITE}{VEHICLE} can't leave depot because it has no power in any tile of the depot
STR_ADVICE_PLATFORM_FREE_PLATFORM :{WHITE}{VEHICLE} can't leave depot because compatible platforms are occupied
STR_ADVICE_PLATFORM_SIGNALS :{WHITE}{VEHICLE} can't leave depot because the segments of the compatible free platforms are occupied. Check the signaling of those segments.
# Depot unbunching related errors # Depot unbunching related errors
STR_ERROR_UNBUNCHING_ONLY_ONE_ALLOWED :{WHITE}... can only have one unbunching order STR_ERROR_UNBUNCHING_ONLY_ONE_ALLOWED :{WHITE}... can only have one unbunching order

View File

@ -21,6 +21,9 @@
struct Train; struct Train;
static const uint8_t _vehicle_initial_x_fract[4] = {10, 8, 4, 8};
static const uint8_t _vehicle_initial_y_fract[4] = { 8, 4, 8, 10};
/** Rail vehicle flags. */ /** Rail vehicle flags. */
enum VehicleRailFlags { enum VehicleRailFlags {
VRF_REVERSING = 0, VRF_REVERSING = 0,
@ -355,5 +358,6 @@ protected: // These functions should not be called outside acceleration code.
bool HasCompatibleDepotTile(TileIndex tile, const Train *t); bool HasCompatibleDepotTile(TileIndex tile, const Train *t);
bool HandleTrainEnterDepot(Train *v); bool HandleTrainEnterDepot(Train *v);
bool CheckReverseTrain(const Train *v);
#endif /* TRAIN_H */ #endif /* TRAIN_H */

View File

@ -41,6 +41,7 @@
#include "depot_base.h" #include "depot_base.h"
#include "platform_func.h" #include "platform_func.h"
#include "depot_map.h" #include "depot_map.h"
#include "train_placement.h"
#include "table/strings.h" #include "table/strings.h"
#include "table/train_sprites.h" #include "table/train_sprites.h"
@ -54,9 +55,6 @@ static TileIndex TrainApproachingCrossingTile(const Train *v);
static void CheckIfTrainNeedsService(Train *v); static void CheckIfTrainNeedsService(Train *v);
static void CheckNextTrainTile(Train *v); static void CheckNextTrainTile(Train *v);
static const uint8_t _vehicle_initial_x_fract[4] = {10, 8, 4, 8};
static const uint8_t _vehicle_initial_y_fract[4] = { 8, 4, 8, 10};
template <> template <>
bool IsValidImageIndex<VEH_TRAIN>(uint8_t image_index) bool IsValidImageIndex<VEH_TRAIN>(uint8_t image_index)
{ {

View File

@ -0,0 +1,312 @@
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file train_placement.cpp Handling of trains in depot platforms. */
#include "stdafx.h"
#include "error.h"
#include "news_func.h"
#include "company_func.h"
#include "strings_func.h"
#include "platform_func.h"
#include "depot_base.h"
#include "depot_map.h"
#include "train_placement.h"
#include "train.h"
#include "table/strings.h"
#include "safeguards.h"
/**
* Check if a train can be placed in a given tile.
* @param train The train.
* @param check_tile The tile where we want to check whether it is possible to place the train.
* @param executing False if testing and true if the call is being executed.
* @return whether it found a platform to place the train.
*/
bool TrainPlacement::CheckPlacement(const Train *train, TileIndex check_tile, bool executing)
{
assert(train != nullptr);
assert(IsRailDepotTile(check_tile));
RailType rt = GetRailType(check_tile);
PlacementInfo error_info = PI_FAILED_FREE_WAGGON;
bool is_extended_depot = IsExtendedRailDepot(check_tile);
bool succeeded = !train->IsFreeWagon();
if (succeeded) {
error_info = PI_FAILED_PLATFORM_TYPE;
for (const Train *t = train; t != nullptr && succeeded; t = t->Next()) {
RailType rail_type = Engine::Get(t->engine_type)->u.rail.railtype;
if (!IsCompatibleRail(rail_type, rt)) succeeded = false;
}
}
if (succeeded && is_extended_depot) {
error_info = PI_FAILED_LENGTH;
if (train->gcache.cached_total_length > GetPlatformLength(check_tile) * TILE_SIZE) succeeded = false;
}
if (succeeded) {
error_info = PI_FAILED_POWER;
bool has_power = false;
for (const Train *t = train; t != nullptr && !has_power; t = t->Next()) {
if (HasPowerOnRail(train->railtype, rt)) has_power = true;
}
if (!has_power) succeeded = false;
}
if (succeeded && is_extended_depot) {
error_info = PI_FAILED_RESERVED;
/* Check whether any tile of the platform is reserved. Don't assume all platform
* is reserved as a whole: sections of the platform may be reserved by crashed trains. */
for (TileIndex tile : GetPlatformTileArea(check_tile)) {
if (HasDepotReservation(tile)) {
succeeded = false;
break;
}
}
}
if (succeeded && executing) {
/* Do not check for signals if really not executing and action. */
error_info = PI_FAILED_SIGNALS;
SigSegState seg_state = UpdateSignalsOnSegment(check_tile, INVALID_DIAGDIR, train->owner);
if (train->force_proceed == TFP_NONE && seg_state == SIGSEG_FULL) succeeded = false;
}
if (succeeded) error_info = PI_SUCCESS;
if (error_info > this->info) {
this->best_tile = check_tile;
this->info = error_info;
/* A direction for the train must be choosen: the one that allows the longest train in platform. */
DiagDirection dir = GetRailDepotDirection(check_tile);
if (is_extended_depot && GetPlatformLength(check_tile, dir) > GetPlatformLength(check_tile, ReverseDiagDir(dir))) {
dir = ReverseDiagDir(dir);
}
this->best_dir = DiagDirToDir(dir);
}
return succeeded;
}
/**
* Before placing a train in the rails of a depot, a valid platform must
* be found. This function finds a tile for placing the train (and also gets the direction and track).
* If there is no valid tile, it will be returned as best_tile == INVALID_TILE or info == PI_FAILED_PLATFORM_TYPE.
* @param t The train we want to place in rails.
* @param executing False if testing and true if the call is being executed.
* @pre The train must be inside the rail depot as if it where in a standard depot.
* (i.e. the track is TRACK_BIT_DEPOT, vehicles are hidden...).
*/
void TrainPlacement::LookForPlaceInDepot(const Train *train, bool executing)
{
assert(train != nullptr);
assert(IsRailDepotTile(train->tile));
/* Initialitzation. */
bool is_extended_depot = IsExtendedRailDepot(train->tile);
this->best_tile = (this->placed || !is_extended_depot) ? train->tile : GetPlatformExtremeTile(train->tile, DirToDiagDir(train->direction));
assert(IsStandardRailDepot(this->best_tile) || IsAnyStartPlatformTile(this->best_tile));
this->best_dir = train->direction;
this->info = PI_BEGIN;
/* First candidate is the original position of the train. */
if (CheckPlacement(train, this->best_tile, executing)) return;
/* Check all platforms. */
Depot *depot = Depot::GetByTile(train->tile);
for (auto &depot_tile : depot->depot_tiles) {
if (CheckPlacement(train, depot_tile, executing)) return;
}
}
/**
* Check if a train can leave now or when other trains
* move away. It returns whether there is a platform long
* enough and with the appropriate rail type.
* @param train The train.
* @param executing False if testing and true if the call is being executed.
* @return true iff there is a compatible platform long enough.
*/
bool TrainPlacement::CanFindAppropriatePlatform(const Train *train, bool executing)
{
this->LookForPlaceInDepot(train, executing);
return this->info >= PI_WONT_LEAVE;
}
/**
* Lift a train in a depot: keep the positions of the elements of the chain if needed,
* and keep also the original tile, direction and track.
* @param train The train we want to lift.
* @pre The train must be inside a rail depot.
* (i.e. the track is 'valid track | TRACK_BIT_DEPOT' or just 'TRACK_BIT_DEPOT').
*/
void TrainPlacement::LiftTrain(Train *train, DoCommandFlag flags)
{
assert(train == nullptr || train->IsInDepot());
assert(train == nullptr || IsRailDepotTile(train->tile));
assert(this->placed == false);
/* Lift the train only if we have a train in an extended depot. */
if (train == nullptr || !IsExtendedRailDepot(train->tile)) return;
/* Do not lift in recursive commands of autoreplace. */
if (flags & DC_AUTOREPLACE) return;
/* If train is not placed... return, because train is already lifted. */
if ((train->track & ~TRACK_BIT_DEPOT) == 0) return;
/* Train is placed in rails: lift it. */
this->placed = true;
if (flags & DC_EXEC) FreeTrainTrackReservation(train);
for (Train *t = train; t != nullptr; t = t->Next()) {
// Lift.
t->track = TRACK_BIT_DEPOT;
t->tile = train->tile;
t->x_pos = train->x_pos;
t->y_pos = train->y_pos;
t->UpdatePosition();
t->UpdateViewport(true, true);
}
if ((flags & DC_EXEC) == 0) return;
SetPlatformReservation(train->tile, false);
UpdateSignalsOnSegment(train->tile, INVALID_DIAGDIR, train->owner);
}
/**
* When a train is lifted inside a depot, before starting its way again,
* must be placed in rails if in an extended rail depot; this function does all necessary things to do so.
* In general, it's the opposite of #LiftTrain
* @param train The train we want to place in rails.
* @param flags Associated command flags
* @pre The train must be inside the extended rail depot as if in a standard depot.
* (i.e. the track is TRACK_BIT_DEPOT, vehicles are hidden...).
*/
void TrainPlacement::PlaceTrain(Train *train, DoCommandFlag flags)
{
if (train == nullptr) return;
if (train != train->First()) return;
if (!IsRailDepotTile(train->tile)) return;
if (flags & DC_AUTOREPLACE) return;
bool executing = (flags & DC_EXEC) != 0;
/* Look for an appropriate platform. */
this->LookForPlaceInDepot(train, executing);
assert(!IsExtendedRailDepot(this->best_tile) || IsAnyStartPlatformTile(this->best_tile));
if (this->info < PI_FAILED_END || !executing) {
if (!executing) {
/* Restore the train. */
this->best_tile = train->tile;
this->best_dir = train->direction;
this->info = PI_SUCCESS;
}
if (!this->placed || (this->info < PI_FAILED_END && executing)) {
for (Train *t = train; t != nullptr; t = t->Next()) {
t->tile = this->best_tile;
t->vehstatus |= VS_HIDDEN;
t->track = TRACK_BIT_DEPOT;
}
if (!executing) return;
train->PowerChanged();
}
if (this->info < PI_FAILED_END && executing) {
/* Train cannot leave until changing the depot. Stop the train and send a message. */
if (info < PI_WONT_LEAVE) {
train->vehstatus |= VS_STOPPED;
/* If vehicle is not stopped and user is the local company, send a message if needed. */
if ((train->vehstatus & VS_STOPPED) == 0 && train->owner == _local_company && train->IsFrontEngine()) {
SetDParam(0, train->index);
AddVehicleAdviceNewsItem(STR_ADVICE_PLATFORM_TYPE + info - PI_ERROR_BEGIN, train->index);
}
}
return;
}
}
assert(this->best_tile != INVALID_TILE);
assert(this->best_dir != INVALID_DIR);
assert(IsRailDepotTile(this->best_tile));
if (executing) {
train->tile = this->best_tile;
train->track = TrackToTrackBits(GetRailDepotTrack(this->best_tile));
train->direction = this->best_dir;
train->PowerChanged();
}
if (IsStandardRailDepot(this->best_tile)) {
int x = TileX(this->best_tile) * TILE_SIZE + _vehicle_initial_x_fract[DirToDiagDir(this->best_dir)];
int y = TileY(this->best_tile) * TILE_SIZE + _vehicle_initial_y_fract[DirToDiagDir(this->best_dir)];
for (Train *t = train; t != nullptr; t = t->Next()) {
t->tile = this->best_tile;
t->direction = this->best_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();
t->UpdateViewport(true, true);
}
return;
}
DiagDirection placing_dir = ReverseDiagDir(DirToDiagDir(this->best_dir));
static const uint8_t _plat_initial_x_fract[4] = {15, 8, 0, 8};
static const uint8_t _plat_initial_y_fract[4] = { 8, 0, 8, 15};
int x = TileX(this->best_tile) * TILE_SIZE | _plat_initial_x_fract[placing_dir];
int y = TileY(this->best_tile) * TILE_SIZE | _plat_initial_y_fract[placing_dir];
/* Add the offset for the first vehicle. */
x += TileIndexDiffCByDiagDir(placing_dir).x * (train->gcache.cached_veh_length + 1) / 2;
y += TileIndexDiffCByDiagDir(placing_dir).y * (train->gcache.cached_veh_length + 1) / 2;
/* Proceed placing the train in the given tile.
* At this point, the first vehicle contains the direction, tile and track.
* We must update positions of all the chain. */
for (Train *t = train; t != nullptr; t = t->Next()) {
t->vehstatus &= ~VS_HIDDEN;
t->direction = this->best_dir;
t->track = DiagDirToDiagTrackBits(placing_dir) | TRACK_BIT_DEPOT;
t->x_pos = x;
t->y_pos = y;
t->z_pos = GetSlopePixelZ(t->x_pos, t->y_pos);
t->tile = TileVirtXY(t->x_pos,t->y_pos);
assert(t->z_pos == train->z_pos);
assert(IsExtendedRailDepotTile(t->tile));
t->UpdatePosition();
t->UpdateViewport(true, true);
int advance = t->CalcNextVehicleOffset();
x += TileIndexDiffCByDiagDir(placing_dir).x * advance;
y += TileIndexDiffCByDiagDir(placing_dir).y * advance;
}
SetPlatformReservation(train->tile, true);
UpdateSignalsOnSegment(train->tile, INVALID_DIAGDIR, train->owner);
}

View File

@ -0,0 +1,58 @@
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file train_placement.h Handling of trains in depot platforms. */
#ifndef TRAIN_PLA_H
#define TRAIN_PLA_H
#include "core/enum_type.hpp"
#include "train.h"
/* Flags of failure and success when placing a train. */
enum PlacementInfo {
PI_BEGIN = 0,
PI_FAILED_FREE_WAGGON = PI_BEGIN, // Free waggon: not to be placed.
PI_ERROR_BEGIN,
PI_FAILED_PLATFORM_TYPE = PI_ERROR_BEGIN, // No compatible platforms with train type.
PI_FAILED_LENGTH, // There are compatible platforms but not long enough.
PI_FAILED_POWER, // No engine gets power in the platform.
PI_WONT_LEAVE,
PI_FAILED_RESERVED = PI_WONT_LEAVE, // There are compatible platforms but reserved right now.
PI_FAILED_SIGNALS, // There are compatible platforms not reserved, but signals don't allow placing it now.
PI_FAILED_END,
PI_SUCCESS = PI_FAILED_END, // There is an appropriate platform.
PI_END,
};
/* Store position of a train and lift it when necessary. */
struct TrainPlacement {
bool placed; // True if train is placed in rails.
TileIndex best_tile; // Best tile for the train.
Direction best_dir; // Best direction for the train.
PlacementInfo info; // Info of possible problems in best platform.
TrainPlacement() : placed(false),
best_tile(INVALID_TILE),
best_dir(INVALID_DIR),
info(PI_FAILED_PLATFORM_TYPE) {}
bool CheckPlacement(const Train *train, TileIndex tile, bool executing);
void LookForPlaceInDepot(const Train *train, bool executing);
bool CanFindAppropriatePlatform(const Train *train, bool executing);
void LiftTrain(Train *train, DoCommandFlag flags);
void PlaceTrain(Train *train, DoCommandFlag flags);
};
static inline bool CheckIfTrainNeedsPlacement(const Train *train)
{
return IsExtendedRailDepot(train->tile) && (train->track & ~TRACK_BIT_DEPOT) == 0;
}
#endif /* TRAIN_PLA_H */