File: PlayTests.cpp

package info (click to toggle)
openrct2 0.4.3%2Bds-1
  • links: PTS, VCS
  • area: contrib
  • in suites: bookworm
  • size: 67,880 kB
  • sloc: cpp: 549,527; ansic: 1,322; sh: 441; python: 269; xml: 180; php: 34; makefile: 19
file content (198 lines) | stat: -rw-r--r-- 6,757 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
/*****************************************************************************
 * Copyright (c) 2014-2022 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/ParkSetParameterAction.h>
#include <openrct2/actions/RideSetPriceAction.h>
#include <openrct2/actions/RideSetStatusAction.h>
#include <openrct2/actions/SetParkEntranceFeeAction.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;
    Platform::CoreInit();

    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);
    importer->Import();

    ResetEntitySpatialIndices();

    reset_all_sprite_quadrant_placements();
    ScenerySetDefaultPlacementConfiguration();
    load_palette();
    EntityTweener::Get().Reset();
    MapAnimationAutoCreate();
    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);
    execute<SetParkEntranceFeeAction>(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
    execute<RideSetStatusAction>(ferrisWheel.id, RideStatus::Open);
    execute<RideSetPriceAction>(ferrisWheel.id, 0, true);

    // Ignore intensity to stimulate peeps to queue into ferris wheel
    gCheatsIgnoreRideIntensity = true;

    // Insert a rich guest
    auto richGuest = gs->GetPark().GenerateGuest();
    richGuest->CashInPocket = 3000;

    // Wait for rich guest to get in queue
    bool matched = updateUntil(*gs, 1000, [&]() { return richGuest->State == PeepState::Queuing; });
    ASSERT_TRUE(matched);

    // Insert poor guest
    auto poorGuest = gs->GetPark().GenerateGuest();
    poorGuest->CashInPocket = 5;

    // Wait for poor guest to get in queue
    matched = updateUntil(*gs, 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(*gs, 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 gs = context->GetGameState();
    ASSERT_NE(gs, nullptr);

    // Open park for free but charging for rides
    execute<ParkSetParameterAction>(ParkParameter::Open);
    execute<SetParkEntranceFeeAction>(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
    execute<RideSetStatusAction>(carRide.id, RideStatus::Open);
    execute<RideSetPriceAction>(carRide.id, 0, true);

    // Ignore intensity 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 == PeepState::OnRide; };
    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();
    }
}