mirror of https://github.com/OpenRCT2/OpenRCT2.git
808 lines
27 KiB
C++
808 lines
27 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 "../windows/Window.h"
|
|
#include "Viewport.h"
|
|
#include "Window.h"
|
|
|
|
#include <algorithm>
|
|
#include <openrct2/Context.h>
|
|
#include <openrct2/Editor.h>
|
|
#include <openrct2/Game.h>
|
|
#include <openrct2/GameState.h>
|
|
#include <openrct2/Input.h>
|
|
#include <openrct2/OpenRCT2.h>
|
|
#include <openrct2/actions/BalloonPressAction.h>
|
|
#include <openrct2/actions/FootpathAdditionRemoveAction.h>
|
|
#include <openrct2/actions/FootpathRemoveAction.h>
|
|
#include <openrct2/actions/LargeSceneryRemoveAction.h>
|
|
#include <openrct2/actions/ParkEntranceRemoveAction.h>
|
|
#include <openrct2/actions/SmallSceneryRemoveAction.h>
|
|
#include <openrct2/actions/WallRemoveAction.h>
|
|
#include <openrct2/entity/Balloon.h>
|
|
#include <openrct2/entity/Duck.h>
|
|
#include <openrct2/entity/EntityRegistry.h>
|
|
#include <openrct2/entity/Staff.h>
|
|
#include <openrct2/localisation/Formatter.h>
|
|
#include <openrct2/localisation/Localisation.h>
|
|
#include <openrct2/object/BannerSceneryEntry.h>
|
|
#include <openrct2/object/LargeSceneryEntry.h>
|
|
#include <openrct2/object/ObjectEntryManager.h>
|
|
#include <openrct2/object/PathAdditionEntry.h>
|
|
#include <openrct2/object/SmallSceneryEntry.h>
|
|
#include <openrct2/object/WallSceneryEntry.h>
|
|
#include <openrct2/ride/Ride.h>
|
|
#include <openrct2/ride/RideConstruction.h>
|
|
#include <openrct2/ride/RideData.h>
|
|
#include <openrct2/ride/Track.h>
|
|
#include <openrct2/ride/Vehicle.h>
|
|
#include <openrct2/scenario/Scenario.h>
|
|
#include <openrct2/windows/Intent.h>
|
|
#include <openrct2/world/Banner.h>
|
|
#include <openrct2/world/Footpath.h>
|
|
#include <openrct2/world/Map.h>
|
|
#include <openrct2/world/Park.h>
|
|
#include <openrct2/world/Scenery.h>
|
|
#include <openrct2/world/Surface.h>
|
|
#include <openrct2/world/Wall.h>
|
|
|
|
static void ViewportInteractionRemoveScenery(const SmallSceneryElement& smallSceneryElement, const CoordsXY& mapCoords);
|
|
static void ViewportInteractionRemoveFootpath(const PathElement& pathElement, const CoordsXY& mapCoords);
|
|
static void ViewportInteractionRemovePathAddition(const PathElement& pathElement, const CoordsXY& mapCoords);
|
|
static void ViewportInteractionRemoveParkWall(const WallElement& wallElement, const CoordsXY& mapCoords);
|
|
static void ViewportInteractionRemoveLargeScenery(const LargeSceneryElement& largeSceneryElement, const CoordsXY& mapCoords);
|
|
static void ViewportInteractionRemoveParkEntrance(const EntranceElement& entranceElement, CoordsXY mapCoords);
|
|
static Peep* ViewportInteractionGetClosestPeep(ScreenCoordsXY screenCoords, int32_t maxDistance);
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x006ED9D0
|
|
*/
|
|
InteractionInfo ViewportInteractionGetItemLeft(const ScreenCoordsXY& screenCoords)
|
|
{
|
|
InteractionInfo info{};
|
|
// No click input for scenario editor or track manager
|
|
if (gScreenFlags & (SCREEN_FLAGS_SCENARIO_EDITOR | SCREEN_FLAGS_TRACK_MANAGER))
|
|
return info;
|
|
|
|
//
|
|
if ((gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) && gEditorStep != EditorStep::RollercoasterDesigner)
|
|
return info;
|
|
|
|
info = GetMapCoordinatesFromPos(
|
|
screenCoords,
|
|
EnumsToFlags(ViewportInteractionItem::Entity, ViewportInteractionItem::Ride, ViewportInteractionItem::ParkEntrance));
|
|
auto tileElement = info.SpriteType != ViewportInteractionItem::Entity ? info.Element : nullptr;
|
|
// Only valid when info.SpriteType == ViewportInteractionItem::Entity, but can't assign nullptr without compiler
|
|
// complaining
|
|
auto sprite = info.Entity;
|
|
|
|
// Allows only balloons to be popped and ducks to be quacked in title screen
|
|
if (gScreenFlags & SCREEN_FLAGS_TITLE_DEMO)
|
|
{
|
|
if (info.SpriteType == ViewportInteractionItem::Entity && (sprite->Is<Balloon>() || sprite->Is<Duck>()))
|
|
return info;
|
|
|
|
info.SpriteType = ViewportInteractionItem::None;
|
|
return info;
|
|
}
|
|
|
|
switch (info.SpriteType)
|
|
{
|
|
case ViewportInteractionItem::Entity:
|
|
switch (sprite->Type)
|
|
{
|
|
case EntityType::Vehicle:
|
|
{
|
|
auto vehicle = sprite->As<Vehicle>();
|
|
if (vehicle != nullptr && !vehicle->IsCableLift())
|
|
vehicle->SetMapToolbar();
|
|
else
|
|
info.SpriteType = ViewportInteractionItem::None;
|
|
}
|
|
break;
|
|
case EntityType::Guest:
|
|
case EntityType::Staff:
|
|
{
|
|
auto peep = sprite->As<Peep>();
|
|
if (peep != nullptr)
|
|
{
|
|
PeepSetMapTooltip(peep);
|
|
}
|
|
else
|
|
{
|
|
info.SpriteType = ViewportInteractionItem::None;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case ViewportInteractionItem::Ride:
|
|
Guard::ArgumentNotNull(tileElement);
|
|
RideSetMapTooltip(*tileElement);
|
|
break;
|
|
case ViewportInteractionItem::ParkEntrance:
|
|
{
|
|
auto& park = OpenRCT2::GetContext()->GetGameState()->GetPark();
|
|
auto parkName = park.Name.c_str();
|
|
|
|
auto ft = Formatter();
|
|
ft.Add<StringId>(STR_STRING);
|
|
ft.Add<const char*>(parkName);
|
|
SetMapTooltip(ft);
|
|
break;
|
|
}
|
|
default:
|
|
info.SpriteType = ViewportInteractionItem::None;
|
|
break;
|
|
}
|
|
|
|
// If nothing is under cursor, find a close by peep
|
|
if (info.SpriteType == ViewportInteractionItem::None)
|
|
{
|
|
auto peep = ViewportInteractionGetClosestPeep(screenCoords, 32);
|
|
if (peep != nullptr)
|
|
{
|
|
info.Entity = peep;
|
|
info.SpriteType = ViewportInteractionItem::Entity;
|
|
info.Loc.x = peep->x;
|
|
info.Loc.y = peep->y;
|
|
PeepSetMapTooltip(peep);
|
|
}
|
|
}
|
|
|
|
return info;
|
|
}
|
|
|
|
bool ViewportInteractionLeftOver(const ScreenCoordsXY& screenCoords)
|
|
{
|
|
auto info = ViewportInteractionGetItemLeft(screenCoords);
|
|
|
|
switch (info.SpriteType)
|
|
{
|
|
case ViewportInteractionItem::Entity:
|
|
case ViewportInteractionItem::Ride:
|
|
case ViewportInteractionItem::ParkEntrance:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool ViewportInteractionLeftClick(const ScreenCoordsXY& screenCoords)
|
|
{
|
|
auto info = ViewportInteractionGetItemLeft(screenCoords);
|
|
|
|
switch (info.SpriteType)
|
|
{
|
|
case ViewportInteractionItem::Entity:
|
|
{
|
|
auto entity = info.Entity;
|
|
switch (entity->Type)
|
|
{
|
|
case EntityType::Vehicle:
|
|
{
|
|
auto intent = Intent(WD_VEHICLE);
|
|
intent.PutExtra(INTENT_EXTRA_VEHICLE, entity);
|
|
ContextOpenIntent(&intent);
|
|
break;
|
|
}
|
|
case EntityType::Guest:
|
|
case EntityType::Staff:
|
|
{
|
|
auto intent = Intent(WindowClass::Peep);
|
|
intent.PutExtra(INTENT_EXTRA_PEEP, entity);
|
|
ContextOpenIntent(&intent);
|
|
break;
|
|
}
|
|
case EntityType::Balloon:
|
|
{
|
|
if (GameIsNotPaused())
|
|
{
|
|
auto balloonPress = BalloonPressAction(entity->Id);
|
|
GameActions::Execute(&balloonPress);
|
|
}
|
|
}
|
|
break;
|
|
case EntityType::Duck:
|
|
{
|
|
if (GameIsNotPaused())
|
|
{
|
|
auto duck = entity->As<Duck>();
|
|
if (duck != nullptr)
|
|
{
|
|
duck->Press();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
case ViewportInteractionItem::Ride:
|
|
{
|
|
auto intent = Intent(WD_TRACK);
|
|
intent.PutExtra(INTENT_EXTRA_TILE_ELEMENT, info.Element);
|
|
ContextOpenIntent(&intent);
|
|
return true;
|
|
}
|
|
case ViewportInteractionItem::ParkEntrance:
|
|
ContextOpenWindow(WindowClass::ParkInformation);
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x006EDE88
|
|
*/
|
|
InteractionInfo ViewportInteractionGetItemRight(const ScreenCoordsXY& screenCoords)
|
|
{
|
|
Ride* ride;
|
|
int32_t i;
|
|
InteractionInfo info{};
|
|
// No click input for title screen or track manager
|
|
if (gScreenFlags & (SCREEN_FLAGS_TITLE_DEMO | SCREEN_FLAGS_TRACK_MANAGER))
|
|
return info;
|
|
|
|
//
|
|
if ((gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) && gEditorStep != EditorStep::RollercoasterDesigner)
|
|
return info;
|
|
|
|
constexpr auto flags = static_cast<int32_t>(
|
|
~EnumsToFlags(ViewportInteractionItem::Terrain, ViewportInteractionItem::Water));
|
|
info = GetMapCoordinatesFromPos(screenCoords, flags);
|
|
auto tileElement = info.Element;
|
|
|
|
switch (info.SpriteType)
|
|
{
|
|
case ViewportInteractionItem::Entity:
|
|
{
|
|
auto sprite = info.Entity;
|
|
if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || sprite->Type != EntityType::Vehicle)
|
|
{
|
|
info.SpriteType = ViewportInteractionItem::None;
|
|
return info;
|
|
}
|
|
|
|
auto vehicle = sprite->As<Vehicle>();
|
|
if (vehicle == nullptr)
|
|
{
|
|
info.SpriteType = ViewportInteractionItem::None;
|
|
return info;
|
|
}
|
|
ride = GetRide(vehicle->ride);
|
|
if (ride != nullptr && ride->status == RideStatus::Closed)
|
|
{
|
|
auto ft = Formatter();
|
|
ft.Add<StringId>(STR_MAP_TOOLTIP_STRINGID_CLICK_TO_MODIFY);
|
|
ride->FormatNameTo(ft);
|
|
SetMapTooltip(ft);
|
|
}
|
|
return info;
|
|
}
|
|
case ViewportInteractionItem::Ride:
|
|
{
|
|
if (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR)
|
|
{
|
|
info.SpriteType = ViewportInteractionItem::None;
|
|
return info;
|
|
}
|
|
if (tileElement->GetType() == TileElementType::Path)
|
|
{
|
|
info.SpriteType = ViewportInteractionItem::None;
|
|
return info;
|
|
}
|
|
|
|
ride = GetRide(tileElement->GetRideIndex());
|
|
if (ride == nullptr)
|
|
{
|
|
info.SpriteType = ViewportInteractionItem::None;
|
|
return info;
|
|
}
|
|
|
|
if (ride->status != RideStatus::Closed)
|
|
return info;
|
|
|
|
auto ft = Formatter();
|
|
ft.Add<StringId>(STR_MAP_TOOLTIP_STRINGID_CLICK_TO_MODIFY);
|
|
|
|
if (tileElement->GetType() == TileElementType::Entrance)
|
|
{
|
|
StringId stringId;
|
|
if (tileElement->AsEntrance()->GetEntranceType() == ENTRANCE_TYPE_RIDE_ENTRANCE)
|
|
{
|
|
if (ride->num_stations > 1)
|
|
{
|
|
stringId = STR_RIDE_STATION_X_ENTRANCE;
|
|
}
|
|
else
|
|
{
|
|
stringId = STR_RIDE_ENTRANCE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ride->num_stations > 1)
|
|
{
|
|
stringId = STR_RIDE_STATION_X_EXIT;
|
|
}
|
|
else
|
|
{
|
|
stringId = STR_RIDE_EXIT;
|
|
}
|
|
}
|
|
ft.Add<StringId>(stringId);
|
|
}
|
|
else if (tileElement->AsTrack()->IsStation())
|
|
{
|
|
StringId stringId;
|
|
if (ride->num_stations > 1)
|
|
{
|
|
stringId = STR_RIDE_STATION_X;
|
|
}
|
|
else
|
|
{
|
|
stringId = STR_RIDE_STATION;
|
|
}
|
|
ft.Add<StringId>(stringId);
|
|
}
|
|
else
|
|
{
|
|
// FIXME: Why does it *2 the value?
|
|
if (!gCheatsSandboxMode && !MapIsLocationOwned({ info.Loc, tileElement->GetBaseZ() * 2 }))
|
|
{
|
|
info.SpriteType = ViewportInteractionItem::None;
|
|
return info;
|
|
}
|
|
|
|
ride->FormatNameTo(ft);
|
|
return info;
|
|
}
|
|
|
|
ride->FormatNameTo(ft);
|
|
|
|
const auto& rtd = ride->GetRideTypeDescriptor();
|
|
ft.Add<StringId>(GetRideComponentName(rtd.NameConvention.station).capitalised);
|
|
|
|
StationIndex::UnderlyingType stationIndex;
|
|
if (tileElement->GetType() == TileElementType::Entrance)
|
|
stationIndex = tileElement->AsEntrance()->GetStationIndex().ToUnderlying();
|
|
else
|
|
stationIndex = tileElement->AsTrack()->GetStationIndex().ToUnderlying();
|
|
|
|
for (i = stationIndex; i >= 0; i--)
|
|
if (ride->GetStations()[i].Start.IsNull())
|
|
stationIndex--;
|
|
stationIndex++;
|
|
ft.Add<uint16_t>(stationIndex);
|
|
SetMapTooltip(ft);
|
|
return info;
|
|
}
|
|
case ViewportInteractionItem::Wall:
|
|
{
|
|
auto* wallEntry = tileElement->AsWall()->GetEntry();
|
|
if (wallEntry->scrolling_mode != SCROLLING_MODE_NONE)
|
|
{
|
|
auto banner = tileElement->AsWall()->GetBanner();
|
|
if (banner != nullptr)
|
|
{
|
|
auto ft = Formatter();
|
|
ft.Add<StringId>(STR_MAP_TOOLTIP_BANNER_STRINGID_STRINGID);
|
|
banner->FormatTextTo(ft);
|
|
ft.Add<StringId>(STR_MAP_TOOLTIP_STRINGID_CLICK_TO_MODIFY);
|
|
ft.Add<StringId>(wallEntry->name);
|
|
SetMapTooltip(ft);
|
|
return info;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case ViewportInteractionItem::LargeScenery:
|
|
{
|
|
auto* sceneryEntry = tileElement->AsLargeScenery()->GetEntry();
|
|
if (sceneryEntry->scrolling_mode != SCROLLING_MODE_NONE)
|
|
{
|
|
auto banner = tileElement->AsLargeScenery()->GetBanner();
|
|
if (banner != nullptr)
|
|
{
|
|
auto ft = Formatter();
|
|
ft.Add<StringId>(STR_MAP_TOOLTIP_BANNER_STRINGID_STRINGID);
|
|
banner->FormatTextTo(ft);
|
|
ft.Add<StringId>(STR_MAP_TOOLTIP_STRINGID_CLICK_TO_MODIFY);
|
|
ft.Add<StringId>(sceneryEntry->name);
|
|
SetMapTooltip(ft);
|
|
return info;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case ViewportInteractionItem::Banner:
|
|
{
|
|
auto banner = tileElement->AsBanner()->GetBanner();
|
|
if (banner != nullptr)
|
|
{
|
|
auto* bannerEntry = OpenRCT2::ObjectManager::GetObjectEntry<BannerSceneryEntry>(banner->type);
|
|
|
|
auto ft = Formatter();
|
|
ft.Add<StringId>(STR_MAP_TOOLTIP_BANNER_STRINGID_STRINGID);
|
|
banner->FormatTextTo(ft, /*addColour*/ true);
|
|
ft.Add<StringId>(STR_MAP_TOOLTIP_STRINGID_CLICK_TO_MODIFY);
|
|
ft.Add<StringId>(bannerEntry->name);
|
|
SetMapTooltip(ft);
|
|
return info;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!(InputTestFlag(INPUT_FLAG_6)) || !(InputTestFlag(INPUT_FLAG_TOOL_ACTIVE)))
|
|
{
|
|
if (WindowFindByClass(WindowClass::RideConstruction) == nullptr && WindowFindByClass(WindowClass::Footpath) == nullptr)
|
|
{
|
|
info.SpriteType = ViewportInteractionItem::None;
|
|
return info;
|
|
}
|
|
}
|
|
|
|
auto ft = Formatter();
|
|
switch (info.SpriteType)
|
|
{
|
|
case ViewportInteractionItem::Scenery:
|
|
{
|
|
auto* sceneryEntry = tileElement->AsSmallScenery()->GetEntry();
|
|
ft.Add<StringId>(STR_MAP_TOOLTIP_STRINGID_CLICK_TO_REMOVE);
|
|
ft.Add<StringId>(sceneryEntry->name);
|
|
SetMapTooltip(ft);
|
|
return info;
|
|
}
|
|
case ViewportInteractionItem::Footpath:
|
|
ft.Add<StringId>(STR_MAP_TOOLTIP_STRINGID_CLICK_TO_REMOVE);
|
|
if (tileElement->AsPath()->IsQueue())
|
|
ft.Add<StringId>(STR_QUEUE_LINE_MAP_TIP);
|
|
else
|
|
ft.Add<StringId>(STR_FOOTPATH_MAP_TIP);
|
|
SetMapTooltip(ft);
|
|
return info;
|
|
|
|
case ViewportInteractionItem::PathAddition:
|
|
{
|
|
auto* pathAddEntry = tileElement->AsPath()->GetAdditionEntry();
|
|
ft.Add<StringId>(STR_MAP_TOOLTIP_STRINGID_CLICK_TO_REMOVE);
|
|
if (tileElement->AsPath()->IsBroken())
|
|
{
|
|
ft.Add<StringId>(STR_BROKEN);
|
|
}
|
|
ft.Add<StringId>(pathAddEntry != nullptr ? pathAddEntry->name : STR_NONE);
|
|
SetMapTooltip(ft);
|
|
return info;
|
|
}
|
|
case ViewportInteractionItem::ParkEntrance:
|
|
if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode)
|
|
break;
|
|
|
|
if (tileElement->GetType() != TileElementType::Entrance)
|
|
break;
|
|
|
|
ft.Add<StringId>(STR_MAP_TOOLTIP_STRINGID_CLICK_TO_REMOVE);
|
|
ft.Add<StringId>(STR_OBJECT_SELECTION_PARK_ENTRANCE);
|
|
SetMapTooltip(ft);
|
|
return info;
|
|
|
|
case ViewportInteractionItem::Wall:
|
|
{
|
|
auto* wallEntry = tileElement->AsWall()->GetEntry();
|
|
ft.Add<StringId>(STR_MAP_TOOLTIP_STRINGID_CLICK_TO_REMOVE);
|
|
ft.Add<StringId>(wallEntry->name);
|
|
SetMapTooltip(ft);
|
|
return info;
|
|
}
|
|
case ViewportInteractionItem::LargeScenery:
|
|
{
|
|
auto* sceneryEntry = tileElement->AsLargeScenery()->GetEntry();
|
|
ft.Add<StringId>(STR_MAP_TOOLTIP_STRINGID_CLICK_TO_REMOVE);
|
|
ft.Add<StringId>(sceneryEntry->name);
|
|
SetMapTooltip(ft);
|
|
return info;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
info.SpriteType = ViewportInteractionItem::None;
|
|
return info;
|
|
}
|
|
|
|
bool ViewportInteractionRightOver(const ScreenCoordsXY& screenCoords)
|
|
{
|
|
auto info = ViewportInteractionGetItemRight(screenCoords);
|
|
|
|
return info.SpriteType != ViewportInteractionItem::None;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x006E8A62
|
|
*/
|
|
bool ViewportInteractionRightClick(const ScreenCoordsXY& screenCoords)
|
|
{
|
|
CoordsXYE tileElement;
|
|
auto info = ViewportInteractionGetItemRight(screenCoords);
|
|
|
|
switch (info.SpriteType)
|
|
{
|
|
case ViewportInteractionItem::None:
|
|
case ViewportInteractionItem::Terrain:
|
|
case ViewportInteractionItem::Water:
|
|
case ViewportInteractionItem::Label:
|
|
return false;
|
|
|
|
case ViewportInteractionItem::Entity:
|
|
{
|
|
auto entity = info.Entity;
|
|
if (entity->Type == EntityType::Vehicle)
|
|
{
|
|
auto vehicle = entity->As<Vehicle>();
|
|
if (vehicle == nullptr)
|
|
{
|
|
break;
|
|
}
|
|
auto ride = GetRide(vehicle->ride);
|
|
if (ride != nullptr)
|
|
{
|
|
RideConstructionStart(*ride);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case ViewportInteractionItem::Ride:
|
|
tileElement = { info.Loc, info.Element };
|
|
RideModify(tileElement);
|
|
break;
|
|
case ViewportInteractionItem::Scenery:
|
|
ViewportInteractionRemoveScenery(*info.Element->AsSmallScenery(), info.Loc);
|
|
break;
|
|
case ViewportInteractionItem::Footpath:
|
|
ViewportInteractionRemoveFootpath(*info.Element->AsPath(), info.Loc);
|
|
break;
|
|
case ViewportInteractionItem::PathAddition:
|
|
ViewportInteractionRemovePathAddition(*info.Element->AsPath(), info.Loc);
|
|
break;
|
|
case ViewportInteractionItem::ParkEntrance:
|
|
ViewportInteractionRemoveParkEntrance(*info.Element->AsEntrance(), info.Loc);
|
|
break;
|
|
case ViewportInteractionItem::Wall:
|
|
ViewportInteractionRemoveParkWall(*info.Element->AsWall(), info.Loc);
|
|
break;
|
|
case ViewportInteractionItem::LargeScenery:
|
|
ViewportInteractionRemoveLargeScenery(*info.Element->AsLargeScenery(), info.Loc);
|
|
break;
|
|
case ViewportInteractionItem::Banner:
|
|
ContextOpenDetailWindow(WD_BANNER, info.Element->AsBanner()->GetIndex().ToUnderlying());
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x006E08D2
|
|
*/
|
|
static void ViewportInteractionRemoveScenery(const SmallSceneryElement& smallSceneryElement, const CoordsXY& mapCoords)
|
|
{
|
|
auto removeSceneryAction = SmallSceneryRemoveAction(
|
|
{ mapCoords.x, mapCoords.y, smallSceneryElement.GetBaseZ() }, smallSceneryElement.GetSceneryQuadrant(),
|
|
smallSceneryElement.GetEntryIndex());
|
|
|
|
GameActions::Execute(&removeSceneryAction);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x006A614A
|
|
*/
|
|
static void ViewportInteractionRemoveFootpath(const PathElement& pathElement, const CoordsXY& mapCoords)
|
|
{
|
|
WindowBase* w = WindowFindByClass(WindowClass::Footpath);
|
|
if (w != nullptr)
|
|
FootpathProvisionalUpdate();
|
|
|
|
TileElement* tileElement2 = MapGetFirstElementAt(mapCoords);
|
|
if (tileElement2 == nullptr)
|
|
return;
|
|
|
|
auto z = pathElement.GetBaseZ();
|
|
do
|
|
{
|
|
if (tileElement2->GetType() == TileElementType::Path && tileElement2->GetBaseZ() == z)
|
|
{
|
|
auto action = FootpathRemoveAction({ mapCoords, z });
|
|
GameActions::Execute(&action);
|
|
break;
|
|
}
|
|
} while (!(tileElement2++)->IsLastForTile());
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x006A61AB
|
|
*/
|
|
static void ViewportInteractionRemovePathAddition(const PathElement& pathElement, const CoordsXY& mapCoords)
|
|
{
|
|
auto footpathAdditionRemoveAction = FootpathAdditionRemoveAction({ mapCoords.x, mapCoords.y, pathElement.GetBaseZ() });
|
|
GameActions::Execute(&footpathAdditionRemoveAction);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x00666C0E
|
|
*/
|
|
void ViewportInteractionRemoveParkEntrance(const EntranceElement& entranceElement, CoordsXY mapCoords)
|
|
{
|
|
int32_t rotation = entranceElement.GetDirectionWithOffset(1);
|
|
switch (entranceElement.GetSequenceIndex())
|
|
{
|
|
case 1:
|
|
mapCoords += CoordsDirectionDelta[rotation];
|
|
break;
|
|
case 2:
|
|
mapCoords -= CoordsDirectionDelta[rotation];
|
|
break;
|
|
}
|
|
auto parkEntranceRemoveAction = ParkEntranceRemoveAction({ mapCoords.x, mapCoords.y, entranceElement.GetBaseZ() });
|
|
GameActions::Execute(&parkEntranceRemoveAction);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x006E57A9
|
|
*/
|
|
static void ViewportInteractionRemoveParkWall(const WallElement& wallElement, const CoordsXY& mapCoords)
|
|
{
|
|
auto* wallEntry = wallElement.GetEntry();
|
|
if (wallEntry->scrolling_mode != SCROLLING_MODE_NONE)
|
|
{
|
|
ContextOpenDetailWindow(WD_SIGN_SMALL, wallElement.GetBannerIndex().ToUnderlying());
|
|
}
|
|
else
|
|
{
|
|
CoordsXYZD wallLocation = { mapCoords.x, mapCoords.y, wallElement.GetBaseZ(), wallElement.GetDirection() };
|
|
auto wallRemoveAction = WallRemoveAction(wallLocation);
|
|
GameActions::Execute(&wallRemoveAction);
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x006B88DC
|
|
*/
|
|
static void ViewportInteractionRemoveLargeScenery(const LargeSceneryElement& largeSceneryElement, const CoordsXY& mapCoords)
|
|
{
|
|
auto* sceneryEntry = largeSceneryElement.GetEntry();
|
|
|
|
if (sceneryEntry->scrolling_mode != SCROLLING_MODE_NONE)
|
|
{
|
|
auto bannerIndex = largeSceneryElement.GetBannerIndex();
|
|
ContextOpenDetailWindow(WD_SIGN, bannerIndex.ToUnderlying());
|
|
}
|
|
else
|
|
{
|
|
auto removeSceneryAction = LargeSceneryRemoveAction(
|
|
{ mapCoords.x, mapCoords.y, largeSceneryElement.GetBaseZ(), largeSceneryElement.GetDirection() },
|
|
largeSceneryElement.GetSequenceIndex());
|
|
GameActions::Execute(&removeSceneryAction);
|
|
}
|
|
}
|
|
|
|
struct PeepDistance
|
|
{
|
|
Peep* peep = nullptr;
|
|
int32_t distance = std::numeric_limits<decltype(distance)>::max();
|
|
};
|
|
|
|
template<typename T>
|
|
PeepDistance GetClosestPeep(const ScreenCoordsXY& viewportCoords, const int32_t maxDistance, PeepDistance goal)
|
|
{
|
|
for (auto peep : EntityList<T>())
|
|
{
|
|
if (peep->x == LOCATION_NULL)
|
|
continue;
|
|
|
|
auto distance = abs(((peep->SpriteData.SpriteRect.GetLeft() + peep->SpriteData.SpriteRect.GetRight()) / 2)
|
|
- viewportCoords.x)
|
|
+ abs(((peep->SpriteData.SpriteRect.GetTop() + peep->SpriteData.SpriteRect.GetBottom()) / 2) - viewportCoords.y);
|
|
if (distance > maxDistance)
|
|
continue;
|
|
|
|
if (distance < goal.distance)
|
|
{
|
|
goal.peep = peep;
|
|
goal.distance = distance;
|
|
}
|
|
}
|
|
return goal;
|
|
}
|
|
|
|
static Peep* ViewportInteractionGetClosestPeep(ScreenCoordsXY screenCoords, int32_t maxDistance)
|
|
{
|
|
auto* w = WindowFindFromPoint(screenCoords);
|
|
if (w == nullptr)
|
|
return nullptr;
|
|
|
|
auto* viewport = w->viewport;
|
|
if (viewport == nullptr || viewport->zoom >= ZoomLevel{ 2 })
|
|
return nullptr;
|
|
|
|
auto viewportCoords = viewport->ScreenToViewportCoord(screenCoords);
|
|
|
|
PeepDistance goal;
|
|
if (!(viewport->flags & VIEWPORT_FLAG_HIDE_GUESTS))
|
|
goal = GetClosestPeep<Guest>(viewportCoords, maxDistance, goal);
|
|
if (!(viewport->flags & VIEWPORT_FLAG_HIDE_STAFF))
|
|
goal = GetClosestPeep<Staff>(viewportCoords, maxDistance, goal);
|
|
return goal.peep;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x0068A15E
|
|
*/
|
|
CoordsXY ViewportInteractionGetTileStartAtCursor(const ScreenCoordsXY& screenCoords)
|
|
{
|
|
WindowBase* window = WindowFindFromPoint(screenCoords);
|
|
if (window == nullptr || window->viewport == nullptr)
|
|
{
|
|
CoordsXY ret{};
|
|
ret.SetNull();
|
|
return ret;
|
|
}
|
|
auto viewport = window->viewport;
|
|
auto info = GetMapCoordinatesFromPosWindow(
|
|
window, screenCoords, EnumsToFlags(ViewportInteractionItem::Terrain, ViewportInteractionItem::Water));
|
|
auto initialPos = info.Loc;
|
|
|
|
if (info.SpriteType == ViewportInteractionItem::None)
|
|
{
|
|
initialPos.SetNull();
|
|
return initialPos;
|
|
}
|
|
|
|
int16_t waterHeight = 0;
|
|
if (info.SpriteType == ViewportInteractionItem::Water)
|
|
{
|
|
waterHeight = info.Element->AsSurface()->GetWaterHeight();
|
|
}
|
|
|
|
auto initialVPPos = viewport->ScreenToViewportCoord(screenCoords);
|
|
CoordsXY mapPos = initialPos + CoordsXY{ 16, 16 };
|
|
|
|
for (int32_t i = 0; i < 5; i++)
|
|
{
|
|
int16_t z = waterHeight;
|
|
if (info.SpriteType != ViewportInteractionItem::Water)
|
|
{
|
|
z = TileElementHeight(mapPos);
|
|
}
|
|
mapPos = ViewportPosToMapPos(initialVPPos, z);
|
|
mapPos.x = std::clamp(mapPos.x, initialPos.x, initialPos.x + 31);
|
|
mapPos.y = std::clamp(mapPos.y, initialPos.y, initialPos.y + 31);
|
|
}
|
|
|
|
return mapPos.ToTileStart();
|
|
}
|