mirror of https://github.com/OpenTTD/OpenTTD
Add: Base files for placing trains in extended depots.
parent
0e34a5afe2
commit
0619cb633a
|
@ -504,6 +504,8 @@ add_files(
|
|||
train.h
|
||||
train_cmd.cpp
|
||||
train_cmd.h
|
||||
train_placement.cpp
|
||||
train_placement.h
|
||||
train_gui.cpp
|
||||
transparency.h
|
||||
transparency_gui.cpp
|
||||
|
|
|
@ -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_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
|
||||
STR_ERROR_UNBUNCHING_ONLY_ONE_ALLOWED :{WHITE}... can only have one unbunching order
|
||||
|
|
|
@ -21,6 +21,9 @@
|
|||
|
||||
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. */
|
||||
enum VehicleRailFlags {
|
||||
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 HandleTrainEnterDepot(Train *v);
|
||||
bool CheckReverseTrain(const Train *v);
|
||||
|
||||
#endif /* TRAIN_H */
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include "depot_base.h"
|
||||
#include "platform_func.h"
|
||||
#include "depot_map.h"
|
||||
#include "train_placement.h"
|
||||
|
||||
#include "table/strings.h"
|
||||
#include "table/train_sprites.h"
|
||||
|
@ -54,9 +55,6 @@ static TileIndex TrainApproachingCrossingTile(const Train *v);
|
|||
static void CheckIfTrainNeedsService(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 <>
|
||||
bool IsValidImageIndex<VEH_TRAIN>(uint8_t image_index)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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 */
|
Loading…
Reference in New Issue