OpenRCT2/src/openrct2/paint/tile_element/Paint.Entrance.cpp

375 lines
13 KiB
C++

/*****************************************************************************
* Copyright (c) 2014-2024 OpenRCT2 developers
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
*
* OpenRCT2 is licensed under the GNU General Public License version 3.
*****************************************************************************/
#include "../Paint.h"
#include "../../Context.h"
#include "../../Game.h"
#include "../../GameState.h"
#include "../../config/Config.h"
#include "../../drawing/LightFX.h"
#include "../../interface/Viewport.h"
#include "../../localisation/Formatter.h"
#include "../../localisation/Formatting.h"
#include "../../localisation/Localisation.h"
#include "../../object/EntranceObject.h"
#include "../../object/ObjectManager.h"
#include "../../object/StationObject.h"
#include "../../profiling/Profiling.h"
#include "../../ride/RideData.h"
#include "../../ride/TrackDesign.h"
#include "../../world/Banner.h"
#include "../../world/Entrance.h"
#include "../../world/Footpath.h"
#include "../../world/Park.h"
#include "../../world/TileInspector.h"
#include "../support/WoodenSupports.h"
#include "Paint.TileElement.h"
using namespace OpenRCT2;
static void PaintRideEntranceExitScrollingText(
PaintSession& session, const EntranceElement& entranceEl, const StationObject& stationObj, Direction direction,
int32_t height)
{
PROFILED_FUNCTION();
if (stationObj.ScrollingMode == SCROLLING_MODE_NONE)
return;
if (entranceEl.GetEntranceType() == ENTRANCE_TYPE_RIDE_EXIT)
return;
auto ride = GetRide(entranceEl.GetRideIndex());
if (ride == nullptr)
return;
auto ft = Formatter();
ft.Add<StringId>(STR_RIDE_ENTRANCE_NAME);
if (ride->status == RideStatus::Open && !(ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN))
{
ride->FormatNameTo(ft);
}
else
{
ft.Add<StringId>(STR_RIDE_ENTRANCE_CLOSED);
}
char text[256];
if (gConfigGeneral.UpperCaseBanners)
{
FormatStringToUpper(text, sizeof(text), STR_BANNER_TEXT_FORMAT, ft.Data());
}
else
{
FormatStringLegacy(text, sizeof(text), STR_BANNER_TEXT_FORMAT, ft.Data());
}
auto stringWidth = GfxGetStringWidth(text, FontStyle::Tiny);
auto scroll = stringWidth > 0 ? (GetGameState().CurrentTicks / 2) % stringWidth : 0;
PaintAddImageAsChild(
session, ScrollingTextSetup(session, STR_BANNER_TEXT_FORMAT, ft, scroll, stationObj.ScrollingMode, COLOUR_BLACK),
{ 0, 0, height + stationObj.Height }, { { 2, 2, height + stationObj.Height }, { 28, 28, 51 } });
}
static void PaintRideEntranceExitLightEffects(PaintSession& session, int32_t height, const EntranceElement& entranceEl)
{
PROFILED_FUNCTION();
if (LightFXIsAvailable())
{
if (entranceEl.GetEntranceType() == ENTRANCE_TYPE_RIDE_ENTRANCE)
{
LightFXAdd3DLightMagicFromDrawingTile(session.MapPosition, 0, 0, height + 45, LightType::Lantern3);
}
switch (entranceEl.GetDirection())
{
case 0:
LightFXAdd3DLightMagicFromDrawingTile(session.MapPosition, 16, 0, height + 16, LightType::Lantern2);
break;
case 1:
LightFXAdd3DLightMagicFromDrawingTile(session.MapPosition, 0, -16, height + 16, LightType::Lantern2);
break;
case 2:
LightFXAdd3DLightMagicFromDrawingTile(session.MapPosition, -16, 0, height + 16, LightType::Lantern2);
break;
case 3:
LightFXAdd3DLightMagicFromDrawingTile(session.MapPosition, 0, 16, height + 16, LightType::Lantern2);
break;
}
}
}
static void PaintRideEntranceExit(PaintSession& session, uint8_t direction, int32_t height, const EntranceElement& entranceEl)
{
PROFILED_FUNCTION();
auto rideIndex = entranceEl.GetRideIndex();
if ((gTrackDesignSaveMode || (session.ViewFlags & VIEWPORT_FLAG_HIGHLIGHT_PATH_ISSUES))
&& (rideIndex != gTrackDesignSaveRideIndex))
{
return;
}
auto ride = GetRide(rideIndex);
if (ride == nullptr)
{
return;
}
auto stationObj = ride->GetStationObject();
if (stationObj == nullptr || stationObj->BaseImageId == ImageIndexUndefined)
{
return;
}
session.InteractionType = ViewportInteractionItem::Ride;
PaintRideEntranceExitLightEffects(session, height, entranceEl);
auto hasGlass = (stationObj->Flags & STATION_OBJECT_FLAGS::IS_TRANSPARENT) != 0;
auto colourPrimary = ride->track_colour[0].main;
auto colourSecondary = ride->track_colour[0].additional;
auto imageTemplate = ImageId(0, colourPrimary, colourSecondary);
ImageId glassImageTemplate;
if (hasGlass)
{
glassImageTemplate = ImageId().WithTransparency(colourPrimary);
}
if (entranceEl.IsGhost())
{
session.InteractionType = ViewportInteractionItem::None;
imageTemplate = ImageId().WithRemap(FilterPaletteID::PaletteGhost);
}
else if (session.SelectedElement == reinterpret_cast<const TileElement*>(&entranceEl))
{
imageTemplate = ImageId().WithRemap(FilterPaletteID::PaletteGhost);
}
// Format modified to stop repeated code
// Each entrance is split into 2 images for drawing
// Certain entrance styles have another 2 images to draw for coloured windows
auto isExit = entranceEl.GetEntranceType() == ENTRANCE_TYPE_RIDE_EXIT;
CoordsXYZ boundBoxLength = {
(direction & 1) ? 2 : 28,
(direction & 1) ? 28 : 2,
isExit ? 32 : 48,
};
// Back
ImageIndex imageIndex = isExit ? stationObj->BaseImageId + direction + 8 : stationObj->BaseImageId + direction;
ImageIndex glassImageIndex = isExit ? stationObj->BaseImageId + direction + 24 : stationObj->BaseImageId + direction + 16;
PaintAddImageAsParent(session, imageTemplate.WithIndex(imageIndex), { 0, 0, height }, { { 2, 2, height }, boundBoxLength });
if (hasGlass)
{
PaintAddImageAsChild(
session, glassImageTemplate.WithIndex(glassImageIndex), { 0, 0, height }, { { 2, 2, height }, boundBoxLength });
}
// Front
imageIndex += 4;
PaintAddImageAsParent(
session, imageTemplate.WithIndex(imageIndex), { 0, 0, height },
{ { (direction & 1) ? 28 : 2, (direction & 1) ? 2 : 28, height }, boundBoxLength });
if (hasGlass)
{
glassImageIndex += 4;
PaintAddImageAsChild(
session, glassImageTemplate.WithIndex(glassImageIndex), { 0, 0, height },
{ { (direction & 1) ? 28 : 2, (direction & 1) ? 2 : 28, height }, boundBoxLength });
}
PaintUtilPushTunnelRotated(session, direction, height, TUNNEL_SQUARE_FLAT);
if (!entranceEl.IsGhost())
PaintRideEntranceExitScrollingText(session, entranceEl, *stationObj, direction, height);
auto supportsImageTemplate = imageTemplate;
if (!entranceEl.IsGhost())
{
supportsImageTemplate = ImageId().WithPrimary(COLOUR_SATURATED_BROWN);
}
WoodenASupportsPaintSetupRotated(
session, WoodenSupportType::Truss, WoodenSupportSubType::NeSw, direction, height, supportsImageTemplate);
height += isExit ? 40 : 56;
PaintUtilSetSegmentSupportHeight(session, kSegmentsAll, 0xFFFF, 0);
PaintUtilSetGeneralSupportHeight(session, height, 0x20);
}
static void PaintParkEntranceScrollingText(
PaintSession& session, const EntranceObject& entrance, Direction direction, int32_t height)
{
PROFILED_FUNCTION();
if ((direction + 1) & (1 << 1))
return;
auto scrollingMode = entrance.GetScrollingMode();
if (scrollingMode == SCROLLING_MODE_NONE)
return;
auto ft = Formatter();
if (GetGameState().Park.Flags & PARK_FLAGS_PARK_OPEN)
{
const auto& park = OpenRCT2::GetGameState().Park;
auto name = park.Name.c_str();
ft.Add<StringId>(STR_STRING);
ft.Add<const char*>(name);
}
else
{
ft.Add<StringId>(STR_BANNER_TEXT_CLOSED);
ft.Add<uint32_t>(0);
}
char text[256];
if (gConfigGeneral.UpperCaseBanners)
{
FormatStringToUpper(text, sizeof(text), STR_BANNER_TEXT_FORMAT, ft.Data());
}
else
{
FormatStringLegacy(text, sizeof(text), STR_BANNER_TEXT_FORMAT, ft.Data());
}
auto stringWidth = GfxGetStringWidth(text, FontStyle::Tiny);
auto scroll = stringWidth > 0 ? (GetGameState().CurrentTicks / 2) % stringWidth : 0;
auto imageIndex = ScrollingTextSetup(
session, STR_BANNER_TEXT_FORMAT, ft, scroll, scrollingMode + direction / 2, COLOUR_BLACK);
auto textHeight = height + entrance.GetTextHeight();
PaintAddImageAsChild(session, imageIndex, { 0, 0, textHeight }, { { 2, 2, textHeight }, { 28, 28, 47 } });
}
static void PaintParkEntranceLightEffects(PaintSession& session)
{
PROFILED_FUNCTION();
if (LightFXIsAvailable())
{
LightFXAdd3DLightMagicFromDrawingTile(session.MapPosition, 0, 0, 155, LightType::Lantern3);
}
}
static void PaintParkEntrance(PaintSession& session, uint8_t direction, int32_t height, const EntranceElement& entranceEl)
{
PROFILED_FUNCTION();
if (gTrackDesignSaveMode || (session.ViewFlags & VIEWPORT_FLAG_HIGHLIGHT_PATH_ISSUES))
return;
PaintParkEntranceLightEffects(session);
session.InteractionType = ViewportInteractionItem::ParkEntrance;
ImageId imageTemplate;
if (entranceEl.IsGhost())
{
session.InteractionType = ViewportInteractionItem::None;
imageTemplate = ImageId().WithRemap(FilterPaletteID::PaletteGhost);
}
else if (session.SelectedElement == reinterpret_cast<const TileElement*>(&entranceEl))
{
imageTemplate = ImageId().WithRemap(FilterPaletteID::PaletteGhost);
}
auto& objManager = GetContext()->GetObjectManager();
auto entrance = reinterpret_cast<EntranceObject*>(objManager.GetLoadedObject(ObjectType::ParkEntrance, 0));
auto sequence = entranceEl.GetSequenceIndex();
switch (sequence)
{
case EntranceSequence::Centre:
{
// Footpath
auto surfaceDescriptor = entranceEl.GetPathSurfaceDescriptor();
if (surfaceDescriptor != nullptr)
{
auto imageIndex = (surfaceDescriptor->Image + 5 * (1 + (direction & 1)));
PaintAddImageAsParent(
session, imageTemplate.WithIndex(imageIndex), { 0, 0, height }, { { 0, 2, height }, { 32, 28, 0 } });
}
// Entrance
if (entrance != nullptr)
{
auto imageIndex = entrance->GetImage(sequence, direction);
PaintAddImageAsParent(
session, imageTemplate.WithIndex(imageIndex), { 0, 0, height }, { { 2, 2, height + 32 }, { 28, 28, 47 } });
if (!entranceEl.IsGhost())
PaintParkEntranceScrollingText(session, *entrance, direction, height);
}
break;
}
case EntranceSequence::Left:
case EntranceSequence::Right:
if (entrance != nullptr)
{
auto imageIndex = entrance->GetImage(sequence, direction);
auto y = ((direction / 2 + sequence / 2) & 1) ? 26 : 32;
PaintAddImageAsParent(
session, imageTemplate.WithIndex(imageIndex), { 0, 0, height }, { { 3, 3, height }, { 26, y, 79 } });
}
break;
}
auto supportsImageTemplate = imageTemplate;
if (!entranceEl.IsGhost())
{
supportsImageTemplate = ImageId().WithPrimary(COLOUR_SATURATED_BROWN);
}
WoodenASupportsPaintSetupRotated(
session, WoodenSupportType::Truss, WoodenSupportSubType::NeSw, direction, height, supportsImageTemplate);
PaintUtilSetSegmentSupportHeight(session, kSegmentsAll, 0xFFFF, 0);
PaintUtilSetGeneralSupportHeight(session, height + 80, 0x20);
}
static void PaintHeightMarkers(PaintSession& session, const EntranceElement& entranceEl, int32_t height)
{
PROFILED_FUNCTION();
if (PaintShouldShowHeightMarkers(session, VIEWPORT_FLAG_PATH_HEIGHTS))
{
if (entranceEl.GetDirections() & 0xF)
{
auto heightMarkerBaseZ = entranceEl.GetBaseZ() + 3;
ImageIndex baseImageIndex = SPR_HEIGHT_MARKER_BASE;
baseImageIndex += heightMarkerBaseZ / 16;
baseImageIndex += GetHeightMarkerOffset();
baseImageIndex -= kMapBaseZ;
auto imageId = ImageId(baseImageIndex, COLOUR_GREY);
PaintAddImageAsParent(session, imageId, { 16, 16, height }, { { 31, 31, heightMarkerBaseZ + 64 }, { 1, 1, 0 } });
}
}
}
void PaintEntrance(PaintSession& session, uint8_t direction, int32_t height, const EntranceElement& entranceElement)
{
PROFILED_FUNCTION();
session.InteractionType = ViewportInteractionItem::Label;
PaintHeightMarkers(session, entranceElement, height);
switch (entranceElement.GetEntranceType())
{
case ENTRANCE_TYPE_RIDE_ENTRANCE:
case ENTRANCE_TYPE_RIDE_EXIT:
PaintRideEntranceExit(session, direction, height, entranceElement);
break;
case ENTRANCE_TYPE_PARK_ENTRANCE:
PaintParkEntrance(session, direction, height, entranceElement);
break;
}
}