OpenRCT2/test/tests/PlayTests.cpp

199 lines
6.6 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 "TestData.h"
#include <gtest/gtest.h>
#include <memory>
#include <openrct2/Context.h>
#include <openrct2/Game.h>
#include <openrct2/GameState.h>
#include <openrct2/OpenRCT2.h>
#include <openrct2/ParkImporter.h>
#include <openrct2/actions/ParkSetEntranceFeeAction.h>
#include <openrct2/actions/ParkSetParameterAction.h>
#include <openrct2/actions/RideSetPriceAction.h>
#include <openrct2/actions/RideSetStatusAction.h>
#include <openrct2/entity/EntityRegistry.h>
#include <openrct2/entity/EntityTweener.h>
#include <openrct2/entity/Peep.h>
#include <openrct2/object/ObjectManager.h>
#include <openrct2/platform/Platform.h>
#include <openrct2/ride/Ride.h>
#include <openrct2/world/MapAnimation.h>
#include <openrct2/world/Park.h>
#include <openrct2/world/Scenery.h>
#include <string>
using namespace OpenRCT2;
class PlayTests : public testing::Test
{
};
static std::unique_ptr<IContext> localStartGame(const std::string& parkPath)
{
gOpenRCT2Headless = true;
gOpenRCT2NoGraphics = true;
auto context = CreateContext();
if (!context->Initialise())
return {};
auto importer = ParkImporter::CreateS6(context->GetObjectRepository());
auto loadResult = importer->LoadSavedGame(parkPath.c_str(), false);
context->GetObjectManager().LoadObjects(loadResult.RequiredObjects);
// TODO: Have a separate GameState and exchange once loaded.
auto& gameState = GetGameState();
importer->Import(gameState);
ResetEntitySpatialIndices();
ResetAllSpriteQuadrantPlacements();
ScenerySetDefaultPlacementConfiguration();
LoadPalette();
EntityTweener::Get().Reset();
MapAnimationAutoCreate();
FixInvalidVehicleSpriteSizes();
gGameSpeed = 1;
return context;
}
template<class Fn> static bool updateUntil(int maxSteps, Fn&& fn)
{
while (maxSteps-- && !fn())
{
gameStateUpdateLogic();
}
return maxSteps > 0;
}
template<class GA, class... Args> static void execute(Args&&... args)
{
GA ga(std::forward<Args>(args)...);
GameActions::Execute(&ga);
}
TEST_F(PlayTests, SecondGuestInQueueShouldNotRideIfNoFunds)
{
/* This test verifies that a guest, when second in queue, won't be forced to enter
* the ride if it has not enough money to pay for it.
* To simulate this scenario, two guests (a rich and a poor) are encouraged to enter
* the ride queue, and then the price is raised such that the second guest in line
* (the poor one) cannot pay. The poor guest should not enter the ride.
*/
std::string initStateFile = TestData::GetParkPath("small_park_with_ferris_wheel.sv6");
auto context = localStartGame(initStateFile);
ASSERT_NE(context.get(), nullptr);
auto& gameState = GetGameState();
// Open park for free but charging for rides
execute<ParkSetParameterAction>(ParkParameter::Open);
execute<ParkSetEntranceFeeAction>(0);
gameState.Park.Flags |= PARK_FLAGS_UNLOCK_ALL_PRICES;
// Find ferris wheel
auto rideManager = GetRideManager();
auto it = std::find_if(
rideManager.begin(), rideManager.end(), [](auto& ride) { return ride.type == RIDE_TYPE_FERRIS_WHEEL; });
ASSERT_NE(it, rideManager.end());
Ride& ferrisWheel = *it;
// Open it for free
execute<RideSetStatusAction>(ferrisWheel.id, RideStatus::Open);
execute<RideSetPriceAction>(ferrisWheel.id, 0, true);
// Ignore intensity to stimulate peeps to queue into ferris wheel
gameState.Cheats.IgnoreRideIntensity = true;
// Insert a rich guest
auto richGuest = Park::GenerateGuest();
richGuest->CashInPocket = 3000;
// Wait for rich guest to get in queue
bool matched = updateUntil(1000, [&]() { return richGuest->State == PeepState::Queuing; });
ASSERT_TRUE(matched);
// Insert poor guest
auto poorGuest = Park::GenerateGuest();
poorGuest->CashInPocket = 5;
// Wait for poor guest to get in queue
matched = updateUntil(1000, [&]() { return poorGuest->State == PeepState::Queuing; });
ASSERT_TRUE(matched);
// Raise the price of the ride to a value poor guest can't pay
execute<RideSetPriceAction>(ferrisWheel.id, 10, true);
// Verify that the poor guest goes back to walking without riding
// since it doesn't have enough money to pay for it
bool enteredTheRide = false;
matched = updateUntil(10000, [&]() {
enteredTheRide |= poorGuest->State == PeepState::OnRide;
return poorGuest->State == PeepState::Walking || enteredTheRide;
});
ASSERT_TRUE(matched);
ASSERT_FALSE(enteredTheRide);
}
TEST_F(PlayTests, CarRideWithOneCarOnlyAcceptsTwoGuests)
{
// This test verifies that a car ride with one car will accept at most two guests
std::string initStateFile = TestData::GetParkPath("small_park_car_ride_one_car.sv6");
auto context = localStartGame(initStateFile);
ASSERT_NE(context.get(), nullptr);
auto& gameState = GetGameState();
// Open park for free but charging for rides
execute<ParkSetParameterAction>(ParkParameter::Open);
execute<ParkSetEntranceFeeAction>(0);
gameState.Park.Flags |= PARK_FLAGS_UNLOCK_ALL_PRICES;
// Find car ride
auto rideManager = GetRideManager();
auto it = std::find_if(rideManager.begin(), rideManager.end(), [](auto& ride) { return ride.type == RIDE_TYPE_CAR_RIDE; });
ASSERT_NE(it, rideManager.end());
Ride& carRide = *it;
// Open it for free
execute<RideSetStatusAction>(carRide.id, RideStatus::Open);
execute<RideSetPriceAction>(carRide.id, 0, true);
// Ignore intensity to stimulate peeps to queue into the ride
gameState.Cheats.IgnoreRideIntensity = true;
// Create some guests
std::vector<Peep*> guests;
for (int i = 0; i < 25; i++)
{
guests.push_back(Park::GenerateGuest());
}
// Wait until one of them is riding
auto guestIsOnRide = [](auto* g) { return g->State == PeepState::OnRide; };
bool matched = updateUntil(10000, [&]() { return std::any_of(guests.begin(), guests.end(), guestIsOnRide); });
ASSERT_TRUE(matched);
// For the next few ticks at most two guests can be on the ride
for (int i = 0; i < 100; i++)
{
int numRiding = std::count_if(guests.begin(), guests.end(), guestIsOnRide);
ASSERT_LE(numRiding, 2);
gameStateUpdateLogic();
}
}