OpenRCT2/test/tests/PlayTests.cpp
Breno Rodrigues Guimarães ab53ddf59f Avoid fast-forwarding peep into the ride
The code being removed in the patch tries to fast forward a peep into the ride when it is the second peep for a vehicle that is used in pairs. Problem is that funds checking does not happen, so it happens that a peep may pay against its will.
Lets say a rich peep enters in line and a poor peep enters in line right after.
If the price of the ride is such that the rich peep can pay and the poor peep can't, it will be dragged into the ride because funds checking only happened for the first.
The second part of the patch just adjusts we consider the vehicle a full car if the second position is filled.

Add test to verify that a peep is not dragged into a ride it can't pay

This test puts two peeps in a Ferris Wheel. The first peep is rich and the second peep is poor. When they are both in line, the ride price is raised so that the poor peep can't pay.
Make sure the poor peep turns back and leaves the ride.

During development, a mistake in the logic would have broken all rides other than ferris wheels in a way that multiple guests could enter the same car.
Also add a test to make sure that is never broken.
2020-04-25 18:27:33 -03:00

196 lines
6.5 KiB
C++

/*****************************************************************************
* Copyright (c) 2014-2020 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 <openrct2/Context.h>
#include <openrct2/Game.h>
#include <openrct2/GameState.h>
#include <openrct2/OpenRCT2.h>
#include <openrct2/ParkImporter.h>
#include <openrct2/actions/ParkSetParameterAction.hpp>
#include <openrct2/actions/RideSetPriceAction.hpp>
#include <openrct2/object/ObjectManager.h>
#include <openrct2/peep/Peep.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 <openrct2/world/Sprite.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;
core_init();
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.data(), loadResult.RequiredObjects.size());
importer->Import();
reset_sprite_spatial_index();
reset_all_sprite_quadrant_placements();
scenery_set_default_placement_configuration();
load_palette();
map_reorganise_elements();
sprite_position_tween_reset();
AutoCreateMapAnimations();
fix_invalid_vehicle_sprite_sizes();
gGameSpeed = 1;
return context;
}
template<class Fn> static bool updateUntil(GameState& gs, int maxSteps, Fn&& fn)
{
while (maxSteps-- && !fn())
{
gs.UpdateLogic();
}
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 gs = context->GetGameState();
ASSERT_NE(gs, nullptr);
// Open park for free but charging for rides
execute<ParkSetParameterAction>(ParkParameter::Open);
park_set_entrance_fee(0);
gParkFlags |= 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
ride_set_status(&ferrisWheel, RIDE_STATUS_OPEN);
execute<RideSetPriceAction>(ferrisWheel.id, 0, true);
// Ignore intesity to stimulate peeps to queue into ferris wheel
gCheatsIgnoreRideIntensity = true;
// Insert a rich guest
auto richGuest = gs->GetPark().GenerateGuest();
richGuest->cash_in_pocket = 3000;
// Wait for rich guest to get in queue
bool matched = updateUntil(*gs, 1000, [&]() { return richGuest->state == PEEP_STATE_QUEUING; });
ASSERT_TRUE(matched);
// Insert poor guest
auto poorGuest = gs->GetPark().GenerateGuest();
poorGuest->cash_in_pocket = 5;
// Wait for poor guest to get in queue
matched = updateUntil(*gs, 1000, [&]() { return poorGuest->state == PEEP_STATE_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(*gs, 10000, [&]() {
enteredTheRide |= poorGuest->state == PEEP_STATE_ON_RIDE;
return poorGuest->state == PEEP_STATE_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 gs = context->GetGameState();
ASSERT_NE(gs, nullptr);
// Open park for free but charging for rides
execute<ParkSetParameterAction>(ParkParameter::Open);
park_set_entrance_fee(0);
gParkFlags |= 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
ride_set_status(&carRide, RIDE_STATUS_OPEN);
execute<RideSetPriceAction>(carRide.id, 0, true);
// Ignore intesity to stimulate peeps to queue into the ride
gCheatsIgnoreRideIntensity = true;
// Create some guests
std::vector<Peep*> guests;
for (int i = 0; i < 25; i++)
{
guests.push_back(gs->GetPark().GenerateGuest());
}
// Wait until one of them is riding
auto guestIsOnRide = [](auto* g) { return g->state == PEEP_STATE_ON_RIDE; };
bool matched = updateUntil(*gs, 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);
gs->UpdateLogic();
}
}