1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2025-12-17 21:12:34 +01:00

Add award plugin APIs (#24468)

* Add award plugin apis

* Address review feedback (use erase_if, extract AwardAdd method)

* Address review feedback (remove redundant comments, make AwardAdd static)

* Address review feedback (bump plugin api version, add changelog entry)
This commit is contained in:
Jan Strauss
2025-07-13 12:04:26 +02:00
committed by GitHub
parent f5fc12b7a8
commit 0522ae848b
14 changed files with 382 additions and 90 deletions

View File

@@ -1,5 +1,6 @@
0.4.25 (in development) 0.4.25 (in development)
------------------------------------------------------------------------ ------------------------------------------------------------------------
- Feature: [#24468] [Plugin] Add awards to plugin API.
- Feature: [#24702] [Plugin] Add bindings for missing cheats (forcedParkRating, ignoreRidePrice, makeAllDestructible). - Feature: [#24702] [Plugin] Add bindings for missing cheats (forcedParkRating, ignoreRidePrice, makeAllDestructible).
- Fix: [#24598] Cannot load .park files that use official legacy footpaths by accident. - Fix: [#24598] Cannot load .park files that use official legacy footpaths by accident.

View File

@@ -4179,6 +4179,53 @@ declare global {
"scenarioCompleteNameInput" | "scenarioCompleteNameInput" |
"unlockAllPrices"; "unlockAllPrices";
type AwardType =
"mostUntidy" |
"mostTidy" |
"bestRollerCoasters" |
"bestValue" |
"mostBeautiful" |
"worstValue" |
"safest" |
"bestStaff" |
"bestFood" |
"worstFood" |
"bestToilets" |
"mostDisappointing" |
"bestWaterRides" |
"bestCustomDesignedRides" |
"mostDazzlingRideColours" |
"mostConfusingLayout" |
"bestGentleRides";
interface Award {
/**
* The type of the award.
*/
readonly type: AwardType;
/**
* The award description.
*/
readonly text: string;
/**
* Number of months this award will remain active.
* Starts at 5, expires at 0.
*/
readonly monthsRemaining: number;
/**
* The sprite of the award.
*/
readonly imageId: number;
/**
* Whether this is a positive or negative award.
*/
readonly positive: boolean;
}
interface Park { interface Park {
cash: number; cash: number;
rating: number; rating: number;
@@ -4326,6 +4373,25 @@ declare global {
* @param type The type of expenditure to get. * @param type The type of expenditure to get.
*/ */
getMonthlyExpenditure(type: ExpenditureType): number[] getMonthlyExpenditure(type: ExpenditureType): number[]
/**
* The current awards of the park.
*/
readonly awards: Award[]
/**
* Clear all awards.
*/
clearAwards(): void
/**
* Grant the given award type to the park.
* Does not check eligibility.
* If the park already has an active award of the given type, the old award will be removed.
* If the park already has 4 active awards, the oldest award will be removed.
* @param type the award type to grant
*/
grantAward(type: AwardType): void
} }
interface Research { interface Research {

View File

@@ -1290,23 +1290,6 @@ namespace OpenRCT2
// Window: Park // Window: Park
STR_ADMISSION_PRICE = 1756, STR_ADMISSION_PRICE = 1756,
STR_ADMISSION_PRICE_PAY_PER_RIDE_TIP = 6014, STR_ADMISSION_PRICE_PAY_PER_RIDE_TIP = 6014,
STR_AWARD_MOST_UNTIDY = 2814,
STR_AWARD_BEST_CUSTOM_DESIGNED_RIDES = STR_AWARD_MOST_UNTIDY + 13,
STR_AWARD_BEST_FOOD = STR_AWARD_MOST_UNTIDY + 8,
STR_AWARD_BEST_GENTLE_RIDES = STR_AWARD_MOST_UNTIDY + 16,
STR_AWARD_BEST_ROLLERCOASTERS = STR_AWARD_MOST_UNTIDY + 2,
STR_AWARD_BEST_STAFF = STR_AWARD_MOST_UNTIDY + 7,
STR_AWARD_BEST_TOILETS = STR_AWARD_MOST_UNTIDY + 10,
STR_AWARD_BEST_VALUE = STR_AWARD_MOST_UNTIDY + 3,
STR_AWARD_BEST_WATER_RIDES = STR_AWARD_MOST_UNTIDY + 12,
STR_AWARD_MOST_BEAUTIFUL = STR_AWARD_MOST_UNTIDY + 4,
STR_AWARD_MOST_CONFUSING_LAYOUT = STR_AWARD_MOST_UNTIDY + 15,
STR_AWARD_MOST_DAZZLING_RIDE_COLOURS = STR_AWARD_MOST_UNTIDY + 14,
STR_AWARD_MOST_DISAPPOINTING = STR_AWARD_MOST_UNTIDY + 11,
STR_AWARD_MOST_TIDY = STR_AWARD_MOST_UNTIDY + 1,
STR_AWARD_SAFEST = STR_AWARD_MOST_UNTIDY + 6,
STR_AWARD_WORST_FOOD = STR_AWARD_MOST_UNTIDY + 9,
STR_AWARD_WORST_VALUE = STR_AWARD_MOST_UNTIDY + 5,
STR_BUY_LAND_AND_CONSTRUCTION_RIGHTS_TIP = 5135, STR_BUY_LAND_AND_CONSTRUCTION_RIGHTS_TIP = 5135,
STR_CLOSE_PARK = 1013, STR_CLOSE_PARK = 1013,
STR_CLOSE_PARK_TIP = 5296, STR_CLOSE_PARK_TIP = 5296,

View File

@@ -164,31 +164,6 @@ namespace OpenRCT2::Ui::Windows
0, 0,
0, 0,
}; };
struct WindowParkAward {
StringId text;
uint32_t sprite;
};
static constexpr WindowParkAward _parkAwards[] = {
{ STR_AWARD_MOST_UNTIDY, SPR_AWARD_MOST_UNTIDY },
{ STR_AWARD_MOST_TIDY, SPR_AWARD_MOST_TIDY },
{ STR_AWARD_BEST_ROLLERCOASTERS, SPR_AWARD_BEST_ROLLERCOASTERS },
{ STR_AWARD_BEST_VALUE, SPR_AWARD_BEST_VALUE },
{ STR_AWARD_MOST_BEAUTIFUL, SPR_AWARD_MOST_BEAUTIFUL },
{ STR_AWARD_WORST_VALUE, SPR_AWARD_WORST_VALUE },
{ STR_AWARD_SAFEST, SPR_AWARD_SAFEST },
{ STR_AWARD_BEST_STAFF, SPR_AWARD_BEST_STAFF },
{ STR_AWARD_BEST_FOOD, SPR_AWARD_BEST_FOOD },
{ STR_AWARD_WORST_FOOD, SPR_AWARD_WORST_FOOD },
{ STR_AWARD_BEST_TOILETS, SPR_AWARD_BEST_TOILETS },
{ STR_AWARD_MOST_DISAPPOINTING, SPR_AWARD_MOST_DISAPPOINTING },
{ STR_AWARD_BEST_WATER_RIDES, SPR_AWARD_BEST_WATER_RIDES },
{ STR_AWARD_BEST_CUSTOM_DESIGNED_RIDES, SPR_AWARD_BEST_CUSTOM_DESIGNED_RIDES },
{ STR_AWARD_MOST_DAZZLING_RIDE_COLOURS, SPR_AWARD_MOST_DAZZLING_RIDE_COLOURS },
{ STR_AWARD_MOST_CONFUSING_LAYOUT, SPR_AWARD_MOST_CONFUSING_LAYOUT },
{ STR_AWARD_BEST_GENTLE_RIDES, SPR_AWARD_BEST_GENTLE_RIDES },
};
// clang-format on // clang-format on
class ParkWindow final : public Window class ParkWindow final : public Window
@@ -1164,8 +1139,8 @@ namespace OpenRCT2::Ui::Windows
for (const auto& award : currentAwards) for (const auto& award : currentAwards)
{ {
GfxDrawSprite(rt, ImageId(_parkAwards[EnumValue(award.Type)].sprite), screenCoords); GfxDrawSprite(rt, ImageId(AwardGetSprite(award.Type)), screenCoords);
DrawTextWrapped(rt, screenCoords + ScreenCoordsXY{ 34, 6 }, 180, _parkAwards[EnumValue(award.Type)].text); DrawTextWrapped(rt, screenCoords + ScreenCoordsXY{ 34, 6 }, 180, AwardGetText(award.Type));
screenCoords.y += 32; screenCoords.y += 32;
} }

View File

@@ -595,6 +595,7 @@
<ClInclude Include="scripting\bindings\ride\ScRideStation.hpp" /> <ClInclude Include="scripting\bindings\ride\ScRideStation.hpp" />
<ClInclude Include="scripting\bindings\ride\ScTrackIterator.h" /> <ClInclude Include="scripting\bindings\ride\ScTrackIterator.h" />
<ClInclude Include="scripting\bindings\ride\ScTrackSegment.h" /> <ClInclude Include="scripting\bindings\ride\ScTrackSegment.h" />
<ClInclude Include="scripting\bindings\world\ScAward.hpp" />
<ClInclude Include="scripting\bindings\world\ScClimate.hpp" /> <ClInclude Include="scripting\bindings\world\ScClimate.hpp" />
<ClInclude Include="scripting\bindings\world\ScDate.hpp" /> <ClInclude Include="scripting\bindings\world\ScDate.hpp" />
<ClInclude Include="scripting\bindings\world\ScMap.hpp" /> <ClInclude Include="scripting\bindings\world\ScMap.hpp" />
@@ -1117,6 +1118,7 @@
<ClCompile Include="scripting\bindings\ride\ScRideStation.cpp" /> <ClCompile Include="scripting\bindings\ride\ScRideStation.cpp" />
<ClCompile Include="scripting\bindings\ride\ScTrackIterator.cpp" /> <ClCompile Include="scripting\bindings\ride\ScTrackIterator.cpp" />
<ClCompile Include="scripting\bindings\ride\ScTrackSegment.cpp" /> <ClCompile Include="scripting\bindings\ride\ScTrackSegment.cpp" />
<ClCompile Include="scripting\bindings\world\ScAward.cpp" />
<ClCompile Include="scripting\bindings\world\ScMap.cpp" /> <ClCompile Include="scripting\bindings\world\ScMap.cpp" />
<ClCompile Include="scripting\bindings\world\ScPark.cpp" /> <ClCompile Include="scripting\bindings\world\ScPark.cpp" />
<ClCompile Include="scripting\bindings\world\ScParkMessage.cpp" /> <ClCompile Include="scripting\bindings\world\ScParkMessage.cpp" />

View File

@@ -1096,6 +1096,23 @@ enum : StringId
STR_PEEPS_CANT_FIND_TOILET = 2811, STR_PEEPS_CANT_FIND_TOILET = 2811,
STR_PEEPS_GETTING_LOST_OR_STUCK = 2812, STR_PEEPS_GETTING_LOST_OR_STUCK = 2812,
STR_ENTRANCE_FEE_TOO_HI = 2813, STR_ENTRANCE_FEE_TOO_HI = 2813,
STR_AWARD_MOST_UNTIDY = 2814,
STR_AWARD_BEST_CUSTOM_DESIGNED_RIDES = STR_AWARD_MOST_UNTIDY + 13,
STR_AWARD_BEST_FOOD = STR_AWARD_MOST_UNTIDY + 8,
STR_AWARD_BEST_GENTLE_RIDES = STR_AWARD_MOST_UNTIDY + 16,
STR_AWARD_BEST_ROLLERCOASTERS = STR_AWARD_MOST_UNTIDY + 2,
STR_AWARD_BEST_STAFF = STR_AWARD_MOST_UNTIDY + 7,
STR_AWARD_BEST_TOILETS = STR_AWARD_MOST_UNTIDY + 10,
STR_AWARD_BEST_VALUE = STR_AWARD_MOST_UNTIDY + 3,
STR_AWARD_BEST_WATER_RIDES = STR_AWARD_MOST_UNTIDY + 12,
STR_AWARD_MOST_BEAUTIFUL = STR_AWARD_MOST_UNTIDY + 4,
STR_AWARD_MOST_CONFUSING_LAYOUT = STR_AWARD_MOST_UNTIDY + 15,
STR_AWARD_MOST_DAZZLING_RIDE_COLOURS = STR_AWARD_MOST_UNTIDY + 14,
STR_AWARD_MOST_DISAPPOINTING = STR_AWARD_MOST_UNTIDY + 11,
STR_AWARD_MOST_TIDY = STR_AWARD_MOST_UNTIDY + 1,
STR_AWARD_SAFEST = STR_AWARD_MOST_UNTIDY + 6,
STR_AWARD_WORST_FOOD = STR_AWARD_MOST_UNTIDY + 9,
STR_AWARD_WORST_VALUE = STR_AWARD_MOST_UNTIDY + 5,
STR_NEWS_ITEM_AWARD_MOST_UNTIDY = 2831, STR_NEWS_ITEM_AWARD_MOST_UNTIDY = 2831,
STR_NEWS_ITEM_MOST_TIDY = STR_NEWS_ITEM_AWARD_MOST_UNTIDY + 1, STR_NEWS_ITEM_MOST_TIDY = STR_NEWS_ITEM_AWARD_MOST_UNTIDY + 1,
STR_NEWS_ITEM_BEST_ROLLERCOASTERS = STR_NEWS_ITEM_AWARD_MOST_UNTIDY + 2, STR_NEWS_ITEM_BEST_ROLLERCOASTERS = STR_NEWS_ITEM_AWARD_MOST_UNTIDY + 2,

View File

@@ -13,7 +13,6 @@
#include "../config/Config.h" #include "../config/Config.h"
#include "../entity/EntityList.h" #include "../entity/EntityList.h"
#include "../entity/Guest.h" #include "../entity/Guest.h"
#include "../localisation/StringIds.h"
#include "../profiling/Profiling.h" #include "../profiling/Profiling.h"
#include "../ride/Ride.h" #include "../ride/Ride.h"
#include "../ride/RideData.h" #include "../ride/RideData.h"
@@ -29,49 +28,55 @@ enum class AwardEffect : uint8_t
negative, negative,
positive positive
}; };
static constexpr AwardEffect kAwardPositiveMap[] = {
AwardEffect::negative, // AwardType::MostUntidy struct AwardData_t
AwardEffect::positive, // AwardType::MostTidy {
AwardEffect::positive, // AwardType::BestRollerCoasters StringId text;
AwardEffect::positive, // AwardType::BestValue StringId news;
AwardEffect::positive, // AwardType::MostBeautiful ImageIndex sprite;
AwardEffect::negative, // AwardType::WorstValue AwardEffect effect;
AwardEffect::positive, // AwardType::Safest
AwardEffect::positive, // AwardType::BestStaff
AwardEffect::positive, // AwardType::BestFood
AwardEffect::negative, // AwardType::WorstFood
AwardEffect::positive, // AwardType::BestToilets
AwardEffect::negative, // AwardType::MostDisappointing
AwardEffect::positive, // AwardType::BestWaterRides
AwardEffect::positive, // AwardType::BestCustomDesignedRides
AwardEffect::positive, // AwardType::MostDazzlingRideColours
AwardEffect::negative, // AwardType::MostConfusingLayout
AwardEffect::positive, // AwardType::BestGentleRides
}; };
static constexpr StringId AwardNewsStrings[] = { // clang-format off
STR_NEWS_ITEM_AWARD_MOST_UNTIDY, static constexpr AwardData_t AwardData[] = {
STR_NEWS_ITEM_MOST_TIDY, { STR_AWARD_MOST_UNTIDY, STR_NEWS_ITEM_AWARD_MOST_UNTIDY, SPR_AWARD_MOST_UNTIDY, AwardEffect::negative },
STR_NEWS_ITEM_BEST_ROLLERCOASTERS, { STR_AWARD_MOST_TIDY, STR_NEWS_ITEM_MOST_TIDY, SPR_AWARD_MOST_TIDY, AwardEffect::positive },
STR_NEWS_ITEM_BEST_VALUE, { STR_AWARD_BEST_ROLLERCOASTERS, STR_NEWS_ITEM_BEST_ROLLERCOASTERS, SPR_AWARD_BEST_ROLLERCOASTERS, AwardEffect::positive },
STR_NEWS_ITEM_MOST_BEAUTIFUL, { STR_AWARD_BEST_VALUE, STR_NEWS_ITEM_BEST_VALUE, SPR_AWARD_BEST_VALUE, AwardEffect::positive },
STR_NEWS_ITEM_WORST_VALUE, { STR_AWARD_MOST_BEAUTIFUL, STR_NEWS_ITEM_MOST_BEAUTIFUL, SPR_AWARD_MOST_BEAUTIFUL, AwardEffect::positive },
STR_NEWS_ITEM_SAFEST, { STR_AWARD_WORST_VALUE, STR_NEWS_ITEM_WORST_VALUE, SPR_AWARD_WORST_VALUE, AwardEffect::negative },
STR_NEWS_ITEM_BEST_STAFF, { STR_AWARD_SAFEST, STR_NEWS_ITEM_SAFEST, SPR_AWARD_SAFEST, AwardEffect::positive },
STR_NEWS_ITEM_BEST_FOOD, { STR_AWARD_BEST_STAFF, STR_NEWS_ITEM_BEST_STAFF, SPR_AWARD_BEST_STAFF, AwardEffect::positive },
STR_NEWS_ITEM_WORST_FOOD, { STR_AWARD_BEST_FOOD, STR_NEWS_ITEM_BEST_FOOD, SPR_AWARD_BEST_FOOD, AwardEffect::positive },
STR_NEWS_ITEM_BEST_TOILETS, { STR_AWARD_WORST_FOOD, STR_NEWS_ITEM_WORST_FOOD, SPR_AWARD_WORST_FOOD, AwardEffect::negative },
STR_NEWS_ITEM_MOST_DISAPPOINTING, { STR_AWARD_BEST_TOILETS, STR_NEWS_ITEM_BEST_TOILETS, SPR_AWARD_BEST_TOILETS, AwardEffect::positive },
STR_NEWS_ITEM_BEST_WATER_RIDES, { STR_AWARD_MOST_DISAPPOINTING, STR_NEWS_ITEM_MOST_DISAPPOINTING, SPR_AWARD_MOST_DISAPPOINTING, AwardEffect::negative },
STR_NEWS_ITEM_BEST_CUSTOM_DESIGNED_RIDES, { STR_AWARD_BEST_WATER_RIDES, STR_NEWS_ITEM_BEST_WATER_RIDES, SPR_AWARD_BEST_WATER_RIDES, AwardEffect::positive },
STR_NEWS_ITEM_MOST_DAZZLING_RIDE_COLOURS, { STR_AWARD_BEST_CUSTOM_DESIGNED_RIDES, STR_NEWS_ITEM_BEST_CUSTOM_DESIGNED_RIDES, SPR_AWARD_BEST_CUSTOM_DESIGNED_RIDES, AwardEffect::positive },
STR_NEWS_ITEM_MOST_CONFUSING_LAYOUT, { STR_AWARD_MOST_DAZZLING_RIDE_COLOURS, STR_NEWS_ITEM_MOST_DAZZLING_RIDE_COLOURS, SPR_AWARD_MOST_DAZZLING_RIDE_COLOURS, AwardEffect::positive },
STR_NEWS_ITEM_BEST_GENTLE_RIDES, { STR_AWARD_MOST_CONFUSING_LAYOUT, STR_NEWS_ITEM_MOST_CONFUSING_LAYOUT, SPR_AWARD_MOST_CONFUSING_LAYOUT, AwardEffect::negative },
{ STR_AWARD_BEST_GENTLE_RIDES, STR_NEWS_ITEM_BEST_GENTLE_RIDES, SPR_AWARD_BEST_GENTLE_RIDES, AwardEffect::positive },
}; };
// clang-format on
bool AwardIsPositive(AwardType type) bool AwardIsPositive(AwardType type)
{ {
return kAwardPositiveMap[EnumValue(type)] == AwardEffect::positive; return AwardData[EnumValue(type)].effect == AwardEffect::positive;
}
ImageIndex AwardGetSprite(AwardType type)
{
return AwardData[EnumValue(type)].sprite;
}
StringId AwardGetText(AwardType type)
{
return AwardData[EnumValue(type)].text;
}
StringId AwardGetNews(AwardType type)
{
return AwardData[EnumValue(type)].news;
} }
#pragma region Award checks #pragma region Award checks
@@ -598,6 +603,16 @@ void AwardReset()
getGameState().currentAwards.clear(); getGameState().currentAwards.clear();
} }
static void AwardAdd(AwardType type)
{
getGameState().currentAwards.push_back(Award{ 5u, type });
if (Config::Get().notifications.ParkAward)
{
News::AddItemToQueue(News::ItemType::award, AwardGetNews(type), 0, {});
}
Ui::GetWindowManager()->InvalidateByClass(WindowClass::ParkInformation);
}
/** /**
* *
* rct2: 0x0066A86C * rct2: 0x0066A86C
@@ -648,14 +663,24 @@ void AwardUpdateAll()
// Check if award is deserved // Check if award is deserved
if (AwardIsDeserved(awardType, activeAwardTypes)) if (AwardIsDeserved(awardType, activeAwardTypes))
{ {
// Add award AwardAdd(awardType);
currentAwards.push_back(Award{ 5u, awardType }); }
if (Config::Get().notifications.ParkAward) }
}
}
void AwardGrant(AwardType type)
{ {
News::AddItemToQueue(News::ItemType::award, AwardNewsStrings[EnumValue(awardType)], 0, {}); auto& currentAwards = getGameState().currentAwards;
}
windowMgr->InvalidateByClass(WindowClass::ParkInformation); // Remove award type if already granted
} std::erase_if(currentAwards, [type](const Award& award) { return award.Type == type; });
}
// Ensure there is space for the award
if (currentAwards.size() >= OpenRCT2::Limits::kMaxAwards)
{
currentAwards.erase(currentAwards.begin());
} }
AwardAdd(type);
} }

View File

@@ -9,6 +9,9 @@
#pragma once #pragma once
#include "../SpriteIds.h"
#include "../localisation/StringIds.h"
#include <cstdint> #include <cstdint>
#include <vector> #include <vector>
@@ -42,5 +45,9 @@ struct Award
}; };
bool AwardIsPositive(AwardType type); bool AwardIsPositive(AwardType type);
ImageIndex AwardGetSprite(AwardType type);
StringId AwardGetText(AwardType type);
StringId AwardGetNews(AwardType type);
void AwardGrant(AwardType type);
void AwardReset(); void AwardReset();
void AwardUpdateAll(); void AwardUpdateAll();

View File

@@ -51,6 +51,7 @@
#include "bindings/object/ScObjectManager.h" #include "bindings/object/ScObjectManager.h"
#include "bindings/ride/ScRide.hpp" #include "bindings/ride/ScRide.hpp"
#include "bindings/ride/ScRideStation.hpp" #include "bindings/ride/ScRideStation.hpp"
#include "bindings/world/ScAward.hpp"
#include "bindings/world/ScClimate.hpp" #include "bindings/world/ScClimate.hpp"
#include "bindings/world/ScDate.hpp" #include "bindings/world/ScDate.hpp"
#include "bindings/world/ScMap.hpp" #include "bindings/world/ScMap.hpp"
@@ -403,6 +404,7 @@ void ScriptEngine::Initialise()
throw std::runtime_error("Script engine already initialised."); throw std::runtime_error("Script engine already initialised.");
auto ctx = static_cast<duk_context*>(_context); auto ctx = static_cast<duk_context*>(_context);
ScAward::Register(ctx);
ScCheats::Register(ctx); ScCheats::Register(ctx);
ScClimate::Register(ctx); ScClimate::Register(ctx);
ScWeatherState::Register(ctx); ScWeatherState::Register(ctx);

View File

@@ -46,7 +46,7 @@ namespace OpenRCT2
namespace OpenRCT2::Scripting namespace OpenRCT2::Scripting
{ {
static constexpr int32_t kPluginApiVersion = 109; static constexpr int32_t kPluginApiVersion = 110;
// Versions marking breaking changes. // Versions marking breaking changes.
static constexpr int32_t kApiVersionPeepDeprecation = 33; static constexpr int32_t kApiVersionPeepDeprecation = 33;

View File

@@ -0,0 +1,90 @@
/*****************************************************************************
* Copyright (c) 2014-2025 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.
*****************************************************************************/
#ifdef ENABLE_SCRIPTING
#include "ScAward.hpp"
#include "../../../GameState.h"
#include "../../../localisation/Formatting.h"
#include "../../../management/Award.h"
#include "../../../windows/Intent.h"
#include "../../Duktape.hpp"
namespace OpenRCT2::Scripting
{
ScAward::ScAward(size_t index)
: _index(index)
{
}
void ScAward::Register(duk_context* ctx)
{
dukglue_register_property(ctx, &ScAward::type_get, nullptr, "type");
dukglue_register_property(ctx, &ScAward::text_get, nullptr, "text");
dukglue_register_property(ctx, &ScAward::positive_get, nullptr, "positive");
dukglue_register_property(ctx, &ScAward::imageId_get, nullptr, "imageId");
dukglue_register_property(ctx, &ScAward::monthsRemaining_get, nullptr, "monthsRemaining");
}
Award* ScAward::GetAward() const
{
return &getGameState().currentAwards[_index];
}
std::string ScAward::type_get() const
{
auto award = GetAward();
if (award == nullptr)
return {};
return AwardTypeToString(award->Type).value_or(std::string());
}
std::string ScAward::text_get() const
{
auto award = GetAward();
if (award == nullptr)
return {};
Formatter ft{};
ft.Add<StringId>(AwardGetText(award->Type));
return FormatStringIDLegacy(STR_STRINGID, ft.Data());
}
uint16_t ScAward::monthsRemaining_get() const
{
auto award = GetAward();
if (award == nullptr)
return {};
return award->Time;
}
bool ScAward::positive_get() const
{
auto award = GetAward();
if (award == nullptr)
return {};
return AwardIsPositive(award->Type);
}
uint32_t ScAward::imageId_get() const
{
auto award = GetAward();
if (award == nullptr)
return {};
return AwardGetSprite(award->Type);
}
} // namespace OpenRCT2::Scripting
#endif

View File

@@ -0,0 +1,85 @@
/*****************************************************************************
* Copyright (c) 2014-2025 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.
*****************************************************************************/
#pragma once
#ifdef ENABLE_SCRIPTING
#include "../../../Context.h"
#include "../../../management/Award.h"
#include "../../Duktape.hpp"
#include "../../ScriptEngine.h"
#include <string>
namespace OpenRCT2::Scripting
{
static constexpr const char* AwardTypes[] = {
"mostUntidy",
"mostTidy",
"bestRollerCoasters",
"bestValue",
"mostBeautiful",
"worstValue",
"safest",
"bestStaff",
"bestFood",
"worstFood",
"bestToilets",
"mostDisappointing",
"bestWaterRides",
"bestCustomDesignedRides",
"mostDazzlingRideColours",
"mostConfusingLayout",
"bestGentleRides",
};
inline std::optional<std::string> AwardTypeToString(AwardType awardType)
{
auto index = static_cast<size_t>(awardType);
if (index < std::size(AwardTypes))
{
return AwardTypes[index];
}
return std::nullopt;
}
inline std::optional<AwardType> StringToAwardType(std::string_view awardType)
{
auto it = std::find(std::begin(AwardTypes), std::end(AwardTypes), awardType);
if (it != std::end(AwardTypes))
{
return std::optional(static_cast<AwardType>(std::distance(std::begin(AwardTypes), it)));
}
return std::nullopt;
}
class ScAward
{
private:
size_t _index{};
public:
ScAward(size_t index);
static void Register(duk_context* ctx);
private:
Award* GetAward() const;
std::string type_get() const;
std::string text_get() const;
uint16_t monthsRemaining_get() const;
bool positive_get() const;
uint32_t imageId_get() const;
};
} // namespace OpenRCT2::Scripting
#endif

View File

@@ -430,6 +430,35 @@ namespace OpenRCT2::Scripting
return result; return result;
} }
std::vector<std::shared_ptr<ScAward>> ScPark::awards_get() const
{
std::vector<std::shared_ptr<ScAward>> result;
auto& gameState = getGameState();
for (size_t i = 0; i < gameState.currentAwards.size(); i++)
{
result.push_back(std::make_shared<ScAward>(i));
}
return result;
}
void ScPark::clearAwards() const
{
ThrowIfGameStateNotMutable();
AwardReset();
}
void ScPark::grantAward(const std::string& awardType) const
{
ThrowIfGameStateNotMutable();
auto optType = StringToAwardType(awardType);
if (optType.has_value())
{
AwardGrant(optType.value());
}
}
void ScPark::Register(duk_context* ctx) void ScPark::Register(duk_context* ctx)
{ {
dukglue_register_property(ctx, &ScPark::cash_get, &ScPark::cash_set, "cash"); dukglue_register_property(ctx, &ScPark::cash_get, &ScPark::cash_set, "cash");
@@ -463,6 +492,9 @@ namespace OpenRCT2::Scripting
dukglue_register_method(ctx, &ScPark::setFlag, "setFlag"); dukglue_register_method(ctx, &ScPark::setFlag, "setFlag");
dukglue_register_method(ctx, &ScPark::postMessage, "postMessage"); dukglue_register_method(ctx, &ScPark::postMessage, "postMessage");
dukglue_register_method(ctx, &ScPark::getMonthlyExpenditure, "getMonthlyExpenditure"); dukglue_register_method(ctx, &ScPark::getMonthlyExpenditure, "getMonthlyExpenditure");
dukglue_register_property(ctx, &ScPark::awards_get, nullptr, "awards");
dukglue_register_method(ctx, &ScPark::clearAwards, "clearAwards");
dukglue_register_method(ctx, &ScPark::grantAward, "grantAward");
} }
} // namespace OpenRCT2::Scripting } // namespace OpenRCT2::Scripting

View File

@@ -13,6 +13,7 @@
#include "../../../Context.h" #include "../../../Context.h"
#include "../../Duktape.hpp" #include "../../Duktape.hpp"
#include "ScAward.hpp"
#include "ScParkMessage.hpp" #include "ScParkMessage.hpp"
#include "ScResearch.hpp" #include "ScResearch.hpp"
@@ -101,6 +102,12 @@ namespace OpenRCT2::Scripting
std::vector<int32_t> getMonthlyExpenditure(const std::string& expenditureType) const; std::vector<int32_t> getMonthlyExpenditure(const std::string& expenditureType) const;
std::vector<std::shared_ptr<ScAward>> awards_get() const;
void clearAwards() const;
void grantAward(const std::string& awardType) const;
static void Register(duk_context* ctx); static void Register(duk_context* ctx);
}; };
} // namespace OpenRCT2::Scripting } // namespace OpenRCT2::Scripting