Merge branch 'openttd'

This commit is contained in:
Pavel Stupnikov
2023-01-03 19:05:48 +04:00
326 changed files with 12923 additions and 8705 deletions

View File

@@ -434,6 +434,21 @@ void Train::UpdateAcceleration()
this->acceleration = Clamp(power / weight * 4, 1, 255);
}
int Train::GetCursorImageOffset() const
{
if (this->gcache.cached_veh_length != 8 && HasBit(this->flags, VRF_REVERSE_DIRECTION) && !HasBit(EngInfo(this->engine_type)->misc_flags, EF_RAIL_FLIPS)) {
int reference_width = TRAININFO_DEFAULT_VEHICLE_WIDTH;
const Engine *e = this->GetEngine();
if (e->GetGRF() != nullptr && is_custom_sprite(e->u.rail.image_index)) {
reference_width = e->GetGRF()->traininfo_vehicle_width;
}
return ScaleSpriteTrad((this->gcache.cached_veh_length - (int)VEHICLE_LENGTH) * reference_width / (int)VEHICLE_LENGTH);
}
return 0;
}
/**
* Get the width of a train vehicle image in the GUI.
* @param offset Additional offset for positioning the sprite; set to nullptr if not needed
@@ -451,10 +466,14 @@ int Train::GetDisplayImageWidth(Point *offset) const
}
if (offset != nullptr) {
offset->x = ScaleGUITrad(reference_width) / 2;
offset->y = ScaleGUITrad(vehicle_pitch);
if (HasBit(this->flags, VRF_REVERSE_DIRECTION) && !HasBit(EngInfo(this->engine_type)->misc_flags, EF_RAIL_FLIPS)) {
offset->x = ScaleSpriteTrad((this->gcache.cached_veh_length - VEHICLE_LENGTH / 2) * reference_width / VEHICLE_LENGTH);
} else {
offset->x = ScaleSpriteTrad(reference_width) / 2;
}
offset->y = ScaleSpriteTrad(vehicle_pitch);
}
return ScaleGUITrad(this->gcache.cached_veh_length * reference_width / VEHICLE_LENGTH);
return ScaleSpriteTrad(this->gcache.cached_veh_length * reference_width / VEHICLE_LENGTH);
}
static SpriteID GetDefaultTrainSprite(uint8 spritenum, Direction direction)
@@ -500,7 +519,7 @@ static void GetRailIcon(EngineID engine, bool rear_head, int &y, EngineImageType
GetCustomVehicleIcon(engine, dir, image_type, result);
if (result->IsValid()) {
if (e->GetGRF() != nullptr) {
y += ScaleGUITrad(e->GetGRF()->traininfo_vehicle_pitch);
y += ScaleSpriteTrad(e->GetGRF()->traininfo_vehicle_pitch);
}
return;
}
@@ -528,11 +547,11 @@ void DrawTrainEngine(int left, int right, int preferred_x, int y, EngineID engin
seqr.GetBounds(&rectr);
preferred_x = Clamp(preferred_x,
left - UnScaleGUI(rectf.left) + ScaleGUITrad(14),
right - UnScaleGUI(rectr.right) - ScaleGUITrad(15));
left - UnScaleGUI(rectf.left) + ScaleSpriteTrad(14),
right - UnScaleGUI(rectr.right) - ScaleSpriteTrad(15));
seqf.Draw(preferred_x - ScaleGUITrad(14), yf, pal, pal == PALETTE_CRASH);
seqr.Draw(preferred_x + ScaleGUITrad(15), yr, pal, pal == PALETTE_CRASH);
seqf.Draw(preferred_x - ScaleSpriteTrad(14), yf, pal, pal == PALETTE_CRASH);
seqr.Draw(preferred_x + ScaleSpriteTrad(15), yr, pal, pal == PALETTE_CRASH);
} else {
VehicleSpriteSeq seq;
GetRailIcon(engine, false, y, image_type, &seq);
@@ -566,8 +585,8 @@ void GetTrainSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs,
Rect rect;
seq.GetBounds(&rect);
width = UnScaleGUI(rect.right - rect.left + 1);
height = UnScaleGUI(rect.bottom - rect.top + 1);
width = UnScaleGUI(rect.Width());
height = UnScaleGUI(rect.Height());
xoffs = UnScaleGUI(rect.left);
yoffs = UnScaleGUI(rect.top);
@@ -576,9 +595,9 @@ void GetTrainSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs,
seq.GetBounds(&rect);
/* Calculate values relative to an imaginary center between the two sprites. */
width = ScaleGUITrad(TRAININFO_DEFAULT_VEHICLE_WIDTH) + UnScaleGUI(rect.right) - xoffs;
height = std::max<uint>(height, UnScaleGUI(rect.bottom - rect.top + 1));
xoffs = xoffs - ScaleGUITrad(TRAININFO_DEFAULT_VEHICLE_WIDTH) / 2;
width = ScaleSpriteTrad(TRAININFO_DEFAULT_VEHICLE_WIDTH) + UnScaleGUI(rect.right) - xoffs;
height = std::max<uint>(height, UnScaleGUI(rect.Height()));
xoffs = xoffs - ScaleSpriteTrad(TRAININFO_DEFAULT_VEHICLE_WIDTH) / 2;
yoffs = std::min(yoffs, UnScaleGUI(rect.top));
}
}
@@ -1438,7 +1457,15 @@ void Train::UpdateDeltaXY()
this->x_bb_offs = 0;
this->y_bb_offs = 0;
if (!IsDiagonalDirection(this->direction)) {
/* Set if flipped and engine is NOT flagged with custom flip handling. */
int flipped = HasBit(this->flags, VRF_REVERSE_DIRECTION) && !HasBit(EngInfo(this->engine_type)->misc_flags, EF_RAIL_FLIPS);
/* If flipped and vehicle length is odd, we need to adjust the bounding box offset slightly. */
int flip_offs = flipped && (this->gcache.cached_veh_length & 1);
Direction dir = this->direction;
if (flipped) dir = ReverseDir(dir);
if (!IsDiagonalDirection(dir)) {
static const int _sign_table[] =
{
/* x, y */
@@ -1448,25 +1475,25 @@ void Train::UpdateDeltaXY()
1, -1, // DIR_W
};
int half_shorten = (VEHICLE_LENGTH - this->gcache.cached_veh_length) / 2;
int half_shorten = (VEHICLE_LENGTH - this->gcache.cached_veh_length + flipped) / 2;
/* For all straight directions, move the bound box to the centre of the vehicle, but keep the size. */
this->x_offs -= half_shorten * _sign_table[this->direction];
this->y_offs -= half_shorten * _sign_table[this->direction + 1];
this->x_extent += this->x_bb_offs = half_shorten * _sign_table[direction];
this->y_extent += this->y_bb_offs = half_shorten * _sign_table[direction + 1];
this->x_offs -= half_shorten * _sign_table[dir];
this->y_offs -= half_shorten * _sign_table[dir + 1];
this->x_extent += this->x_bb_offs = half_shorten * _sign_table[dir];
this->y_extent += this->y_bb_offs = half_shorten * _sign_table[dir + 1];
} else {
switch (this->direction) {
switch (dir) {
/* Shorten southern corner of the bounding box according the vehicle length
* and center the bounding box on the vehicle. */
case DIR_NE:
this->x_offs = 1 - (this->gcache.cached_veh_length + 1) / 2;
this->x_offs = 1 - (this->gcache.cached_veh_length + 1) / 2 + flip_offs;
this->x_extent = this->gcache.cached_veh_length - 1;
this->x_bb_offs = -1;
break;
case DIR_NW:
this->y_offs = 1 - (this->gcache.cached_veh_length + 1) / 2;
this->y_offs = 1 - (this->gcache.cached_veh_length + 1) / 2 + flip_offs;
this->y_extent = this->gcache.cached_veh_length - 1;
this->y_bb_offs = -1;
break;
@@ -1474,13 +1501,13 @@ void Train::UpdateDeltaXY()
/* Move northern corner of the bounding box down according to vehicle length
* and center the bounding box on the vehicle. */
case DIR_SW:
this->x_offs = 1 + (this->gcache.cached_veh_length + 1) / 2 - VEHICLE_LENGTH;
this->x_offs = 1 + (this->gcache.cached_veh_length + 1) / 2 - VEHICLE_LENGTH - flip_offs;
this->x_extent = VEHICLE_LENGTH - 1;
this->x_bb_offs = VEHICLE_LENGTH - this->gcache.cached_veh_length - 1;
break;
case DIR_SE:
this->y_offs = 1 + (this->gcache.cached_veh_length + 1) / 2 - VEHICLE_LENGTH;
this->y_offs = 1 + (this->gcache.cached_veh_length + 1) / 2 - VEHICLE_LENGTH - flip_offs;
this->y_extent = VEHICLE_LENGTH - 1;
this->y_bb_offs = VEHICLE_LENGTH - this->gcache.cached_veh_length - 1;
break;
@@ -1674,29 +1701,88 @@ static bool TrainApproachingCrossing(TileIndex tile)
return HasVehicleOnPos(tile_from, &tile, &TrainApproachingCrossingEnum);
}
/**
* Check if a level crossing should be barred.
* @param tile The tile to check.
* @return True if the crossing should be barred, else false.
*/
static inline bool CheckLevelCrossing(TileIndex tile)
{
/* reserved || train on crossing || train approaching crossing */
return HasCrossingReservation(tile) || HasVehicleOnPos(tile, NULL, &TrainOnTileEnum) || TrainApproachingCrossing(tile);
}
/**
* Sets correct crossing state
* @param tile tile to update
* @param sound should we play sound?
* @pre tile is a rail-road crossing
* Sets a level crossing tile to the correct state.
* @param tile Tile to update.
* @param sound Should we play sound?
* @param force_barred Should we set the crossing to barred?
* @pre tile is a rail-road crossing.
*/
void UpdateLevelCrossing(TileIndex tile, bool sound)
static void UpdateLevelCrossingTile(TileIndex tile, bool sound, bool force_barred)
{
assert(IsLevelCrossingTile(tile));
bool set_barred;
/* reserved || train on crossing || train approaching crossing */
bool new_state = HasCrossingReservation(tile) || HasVehicleOnPos(tile, nullptr, &TrainOnTileEnum) || TrainApproachingCrossing(tile);
/* We force the crossing to be barred when an adjacent crossing is barred, otherwise let it decide for itself. */
set_barred = force_barred || CheckLevelCrossing(tile);
if (new_state != IsCrossingBarred(tile)) {
if (new_state && sound) {
if (_settings_client.sound.ambient) SndPlayTileFx(SND_0E_LEVEL_CROSSING, tile);
}
SetCrossingBarred(tile, new_state);
/* The state has changed */
if (set_barred != IsCrossingBarred(tile)) {
if (set_barred && sound && _settings_client.sound.ambient) SndPlayTileFx(SND_0E_LEVEL_CROSSING, tile);
SetCrossingBarred(tile, set_barred);
MarkTileDirtyByTile(tile);
}
}
/**
* Update a level crossing to barred or open (crossing may include multiple adjacent tiles).
* @param tile Tile which causes the update.
* @param sound Should we play sound?
* @param force_bar Should we force the crossing to be barred?
*/
void UpdateLevelCrossing(TileIndex tile, bool sound, bool force_bar)
{
if (!IsLevelCrossingTile(tile)) return;
bool forced_state = force_bar;
const Axis axis = GetCrossingRoadAxis(tile);
const DiagDirection dir1 = AxisToDiagDir(axis);
const DiagDirection dir2 = ReverseDiagDir(dir1);
/* Check if an adjacent crossing is barred. */
for (DiagDirection dir : { dir1, dir2 }) {
for (TileIndex t = tile; !forced_state && t < MapSize() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == axis; t = TileAddByDiagDir(t, dir)) {
forced_state |= CheckLevelCrossing(t);
}
}
/* Now that we know whether all tiles in this crossing should be barred or open,
* we need to update those tiles. We start with the tile itself, then look along the road axis. */
UpdateLevelCrossingTile(tile, sound, forced_state);
for (DiagDirection dir : { dir1, dir2 }) {
for (TileIndex t = TileAddByDiagDir(tile, dir); t < MapSize() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == axis; t = TileAddByDiagDir(t, dir)) {
UpdateLevelCrossingTile(t, sound, forced_state);
}
}
}
/**
* Find adjacent level crossing tiles in this multi-track crossing and mark them dirty.
* @param The tile which causes the update.
*/
void MarkDirtyAdjacentLevelCrossingTiles(TileIndex tile, Axis road_axis)
{
const DiagDirection dir1 = AxisToDiagDir(road_axis);
const DiagDirection dir2 = ReverseDiagDir(dir1);
for (DiagDirection dir : { dir1, dir2 }) {
const TileIndex t = TileAddByDiagDir(tile, dir);
if (t < MapSize() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == road_axis) {
MarkTileDirtyByTile(t);
}
}
}
/**
* Bars crossing and plays ding-ding sound if not barred already
@@ -1706,9 +1792,8 @@ void UpdateLevelCrossing(TileIndex tile, bool sound)
static inline void MaybeBarCrossingWithSound(TileIndex tile)
{
if (!IsCrossingBarred(tile)) {
BarCrossing(tile);
if (_settings_client.sound.ambient) SndPlayTileFx(SND_0E_LEVEL_CROSSING, tile);
MarkTileDirtyByTile(tile);
SetCrossingReservation(tile, true);
UpdateLevelCrossing(tile, true);
}
}
@@ -1917,7 +2002,6 @@ CommandCost CmdReverseTrainDirection(DoCommandFlag flags, VehicleID veh_id, bool
if (v->IsMultiheaded() || HasBit(EngInfo(v->engine_type)->callback_mask, CBM_VEHICLE_ARTIC_ENGINE)) {
return_cmd_error(STR_ERROR_CAN_T_REVERSE_DIRECTION_RAIL_VEHICLE_MULTIPLE_UNITS);
}
if (!HasBit(EngInfo(v->engine_type)->misc_flags, EF_RAIL_FLIPS)) return CMD_ERROR;
Train *front = v->First();
/* make sure the vehicle is stopped in the depot */
@@ -2042,7 +2126,7 @@ bool Train::FindClosestDepot(TileIndex *location, DestinationID *destination, bo
}
/** Play a sound for a train leaving the station. */
void Train::PlayLeaveStationSound() const
void Train::PlayLeaveStationSound(bool force) const
{
static const SoundFx sfx[] = {
SND_04_DEPARTURE_STEAM,
@@ -2052,10 +2136,9 @@ void Train::PlayLeaveStationSound() const
SND_41_DEPARTURE_MAGLEV
};
if (PlayVehicleSound(this, VSE_START)) return;
if (PlayVehicleSound(this, VSE_START, force)) return;
EngineID engtype = this->engine_type;
SndPlayVehicleFx(sfx[RailVehInfo(engtype)->engclass], this);
SndPlayVehicleFx(sfx[RailVehInfo(this->engine_type)->engclass], this);
}
/**
@@ -4062,6 +4145,23 @@ Trackdir Train::GetVehicleTrackdir() const
return TrackDirectionToTrackdir(FindFirstTrack(this->track), this->direction);
}
uint16 Train::GetMaxWeight() const
{
uint16 weight = CargoSpec::Get(this->cargo_type)->WeightOfNUnitsInTrain(this->GetEngine()->DetermineCapacity(this));
/* Vehicle weight is not added for articulated parts. */
if (!this->IsArticulatedPart()) {
weight += GetVehicleProperty(this, PROP_TRAIN_WEIGHT, RailVehInfo(this->engine_type)->weight);
}
/* Powered wagons have extra weight added. */
if (HasBit(this->flags, VRF_POWEREDWAGON)) {
weight += RailVehInfo(this->gcache.first_engine)->pow_wag_weight;
}
return weight;
}
namespace citymania {
auto GetDefaultTrainSprite = &::GetDefaultTrainSprite;
}