mirror of
https://github.com/OpenRCT2/OpenRCT2.git
synced 2024-08-30 14:06:07 +02:00
![Breno Rodrigues Guimarães](/assets/img/avatar_default.png)
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.
196 lines
6.5 KiB
C++
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();
|
|
}
|
|
}
|