1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2025-12-23 15:52:55 +01:00

Add research plugin API

This commit is contained in:
Ted John
2023-04-14 21:57:38 +01:00
parent 2fd3eb47e7
commit f35e595d0e
7 changed files with 415 additions and 2 deletions

View File

@@ -2123,7 +2123,7 @@ declare global {
* The track segment adds to inversion counter. Usually applied to the first half of inversions.
*/
readonly countsAsInversion: boolean;
/**
* Gets a length of the subpositions list for this track segment.
*/
@@ -3150,6 +3150,103 @@ declare global {
postMessage(message: ParkMessageDesc): void;
}
interface Research {
/**
* The list of rides and scenery sets that have already been researched.
*/
inventedItems: ResearchItemType[];
/**
* The order of rides and scenery sets to be researched.
*/
uninventedItems: ResearchItemType[];
/**
* The amount of funding currently spent on research.
*/
funding: ResearchFundingLevel;
/**
* Flags representing which research categories are enabled.
*/
priorities: number;
/**
* The current stage for the ride or scenery set being researched.
*/
stage: ResearchFundingStage;
/**
* The progress for the current stage between 0 and 65535.
* This will increment more quickly the higher the research funding.
*/
progress: number;
/**
* The expected month the current item being researched will complete.
* Value is between 0 and 7, 0 being March and 7 being October.
* Value is null if there is not yet an expected month.
*/
readonly expectedMonth: number | null;
/**
* The expected day of the month the current item being researched will complete.
* Value is between 0 and 30, add 1 to it for the human readable date.
* Value is null if there is not yet an expected month.
*/
readonly expectedDay: number | null;
/**
* Gets whether a particular object has been researched and is available to construct.
* @param type The type of object, e.g. ride, scenery group, or small scenery.
* @param index The object index.
*/
isObjectResearched(type: ObjectType, index: number): boolean;
}
interface ResearchItem {
/**
* The research category this item belongs in.
* E.g. gentle rides, thrill rides, shops etc.
*/
category: ResearchCategory;
/**
* Weather the research item is a ride or scenery set.
*/
type: ResearchItemType;
/**
* The ride or scenery set object index.
*/
object: number;
}
type ResearchItemType = "scenery" | "ride";
enum ResearchCategory {
Transport,
Gentle,
Rollercoaster,
Thrill,
Water,
Shop
}
enum ResearchFundingLevel {
None,
Minimum,
Normal,
Maximum
}
type ResearchFundingStage =
"initial_research" |
"designing" |
"completing_design" |
"unknown" |
"finished_all";
type ScenarioObjectiveType =
"none" |
"guestsBy" |

View File

@@ -519,6 +519,25 @@ bool ResearchInsertSceneryGroupEntry(ObjectEntryIndex entryIndex, bool researche
return false;
}
bool ResearchIsInvented(ObjectType objectType, ObjectEntryIndex index)
{
switch (objectType)
{
case ObjectType::Ride:
return RideEntryIsInvented(index);
case ObjectType::SceneryGroup:
return SceneryGroupIsInvented(index);
case ObjectType::SmallScenery:
case ObjectType::LargeScenery:
case ObjectType::Walls:
case ObjectType::Banners:
case ObjectType::PathBits:
return SceneryIsInvented({ static_cast<uint8_t>(objectType), index });
default:
return true;
}
}
bool RideTypeIsInvented(uint32_t rideType)
{
return RideTypeIsValid(rideType) ? _researchedRideTypes[rideType] : false;

View File

@@ -138,6 +138,8 @@ bool ResearchInsertRideEntry(ride_type_t rideType, ObjectEntryIndex entryIndex,
void ResearchInsertRideEntry(ObjectEntryIndex entryIndex, bool researched);
bool ResearchInsertSceneryGroupEntry(ObjectEntryIndex entryIndex, bool researched);
bool ResearchIsInvented(ObjectType objectType, ObjectEntryIndex index);
bool ResearchSetInvented(ObjectType objectType, ObjectEntryIndex index, bool value);
void RideTypeSetInvented(uint32_t rideType);
void RideEntrySetInvented(ObjectEntryIndex rideEntryIndex);
void ScenerySetInvented(const ScenerySelection& sceneryItem);

View File

@@ -47,6 +47,7 @@
# include "bindings/world/ScMap.hpp"
# include "bindings/world/ScPark.hpp"
# include "bindings/world/ScParkMessage.hpp"
# include "bindings/world/ScResearch.hpp"
# include "bindings/world/ScScenario.hpp"
# include "bindings/world/ScTile.hpp"
# include "bindings/world/ScTileElement.hpp"
@@ -409,6 +410,7 @@ void ScriptEngine::Initialise()
ScPlayer::Register(ctx);
ScPlayerGroup::Register(ctx);
ScProfiler::Register(ctx);
ScResearch::Register(ctx);
ScRide::Register(ctx);
ScRideStation::Register(ctx);
ScRideObject::Register(ctx);
@@ -439,7 +441,7 @@ void ScriptEngine::Initialise()
dukglue_register_global(ctx, std::make_shared<ScDate>(), "date");
dukglue_register_global(ctx, std::make_shared<ScMap>(ctx), "map");
dukglue_register_global(ctx, std::make_shared<ScNetwork>(ctx), "network");
dukglue_register_global(ctx, std::make_shared<ScPark>(), "park");
dukglue_register_global(ctx, std::make_shared<ScPark>(ctx), "park");
dukglue_register_global(ctx, std::make_shared<ScProfiler>(ctx), "profiler");
dukglue_register_global(ctx, std::make_shared<ScScenario>(), "scenario");

View File

@@ -44,6 +44,11 @@ namespace OpenRCT2::Scripting
{ "unlockAllPrices", PARK_FLAGS_UNLOCK_ALL_PRICES },
});
ScPark::ScPark(duk_context* ctx)
: _context(ctx)
{
}
money64 ScPark::cash_get() const
{
return gCash;
@@ -294,6 +299,11 @@ namespace OpenRCT2::Scripting
GfxInvalidateScreen();
}
std::shared_ptr<ScResearch> ScPark::research_get() const
{
return std::make_shared<ScResearch>(_context);
}
std::vector<std::shared_ptr<ScParkMessage>> ScPark::messages_get() const
{
std::vector<std::shared_ptr<ScParkMessage>> result;
@@ -405,6 +415,7 @@ namespace OpenRCT2::Scripting
ctx, &ScPark::constructionRightsPrice_get, &ScPark::constructionRightsPrice_set, "constructionRightsPrice");
dukglue_register_property(ctx, &ScPark::parkSize_get, nullptr, "parkSize");
dukglue_register_property(ctx, &ScPark::name_get, &ScPark::name_set, "name");
dukglue_register_property(ctx, &ScPark::research_get, nullptr, "research");
dukglue_register_property(ctx, &ScPark::messages_get, &ScPark::messages_set, "messages");
dukglue_register_property(ctx, &ScPark::casualtyPenalty_get, &ScPark::casualtyPenalty_set, "casualtyPenalty");
dukglue_register_method(ctx, &ScPark::getFlag, "getFlag");

View File

@@ -15,6 +15,7 @@
# include "../../../common.h"
# include "../../Duktape.hpp"
# include "ScParkMessage.hpp"
# include "ScResearch.hpp"
# include <algorithm>
# include <vector>
@@ -23,7 +24,12 @@ namespace OpenRCT2::Scripting
{
class ScPark
{
private:
duk_context* _context;
public:
ScPark(duk_context* ctx);
money64 cash_get() const;
void cash_set(money64 value);
@@ -85,6 +91,8 @@ namespace OpenRCT2::Scripting
void setFlag(const std::string& key, bool value);
std::shared_ptr<ScResearch> research_get() const;
std::vector<std::shared_ptr<ScParkMessage>> messages_get() const;
void messages_set(const std::vector<DukValue>& value);

View File

@@ -0,0 +1,274 @@
/*****************************************************************************
* Copyright (c) 2014-2023 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 "../../../common.h"
# include "../../../core/String.hpp"
# include "../../../management/Research.h"
# include "../../../ride/RideData.h"
# include "../../Duktape.hpp"
# include "../../ScriptEngine.h"
# include "../object/ScObject.hpp"
namespace OpenRCT2::Scripting
{
static const DukEnumMap<uint8_t> ResearchStageMap({
{ "initial_research", RESEARCH_STAGE_INITIAL_RESEARCH },
{ "designing", RESEARCH_STAGE_DESIGNING },
{ "completing_design", RESEARCH_STAGE_COMPLETING_DESIGN },
{ "unknown", RESEARCH_STAGE_UNKNOWN },
{ "finished_all", RESEARCH_STAGE_FINISHED_ALL },
});
static const DukEnumMap<ResearchCategory> ResearchCategoryMap({
{ "transport", ResearchCategory::Transport },
{ "gentle", ResearchCategory::Gentle },
{ "rollercoaster", ResearchCategory::Rollercoaster },
{ "thrill", ResearchCategory::Thrill },
{ "water", ResearchCategory::Water },
{ "shop", ResearchCategory::Shop },
{ "scenery_group", ResearchCategory::SceneryGroup },
});
static const DukEnumMap<Research::EntryType> ResearchEntryTypeMap({
{ "ride", Research::EntryType::Ride },
{ "scenery", Research::EntryType::Scenery },
});
template<> inline DukValue ToDuk(duk_context* ctx, const ResearchItem& value)
{
DukObject obj(ctx);
obj.Set("category", ResearchCategoryMap[value.category]);
obj.Set("type", ResearchEntryTypeMap[value.type]);
if (value.type == Research::EntryType::Ride)
{
obj.Set("rideType", value.baseRideType);
}
obj.Set("object", value.entryIndex);
return obj.Take();
}
template<> Research::EntryType inline FromDuk(const DukValue& d)
{
if (d.type() == DukValue::STRING)
{
auto it = ResearchEntryTypeMap.find(d.as_string());
if (it != ResearchEntryTypeMap.end())
{
return it->second;
}
}
return Research::EntryType::Scenery;
}
template<> ResearchItem inline FromDuk(const DukValue& d)
{
ResearchItem result;
result.baseRideType = 0;
result.category = ResearchCategory::Transport;
result.flags = 0;
result.type = FromDuk<Research::EntryType>(d["type"]);
auto baseRideType = d["rideType"];
if (baseRideType.type() == DukValue::NUMBER)
result.baseRideType = baseRideType.as_int();
result.entryIndex = d["object"].as_int();
return result;
}
class ScResearch
{
private:
duk_context* _context;
public:
ScResearch(duk_context* ctx)
: _context(ctx)
{
}
uint8_t funding_get() const
{
return gResearchFundingLevel;
}
void funding_set(uint8_t value)
{
ThrowIfGameStateNotMutable();
gResearchFundingLevel = value;
}
uint8_t priorities_get() const
{
return gResearchPriorities;
}
void priorities_set(uint8_t value)
{
ThrowIfGameStateNotMutable();
gResearchPriorities = value;
}
std::string stage_get() const
{
return std::string(ResearchStageMap[gResearchProgressStage]);
}
void stage_set(const std::string& value)
{
ThrowIfGameStateNotMutable();
auto it = ResearchStageMap.find(value);
if (it != ResearchStageMap.end())
{
gResearchProgressStage = it->second;
}
}
uint16_t progress_get() const
{
return gResearchProgress;
}
void progress_set(uint16_t value)
{
ThrowIfGameStateNotMutable();
gResearchProgress = value;
}
DukValue expectedMonth_get() const
{
if (gResearchProgressStage == RESEARCH_STAGE_INITIAL_RESEARCH || gResearchExpectedDay == 255)
return ToDuk(_context, nullptr);
return ToDuk(_context, gResearchExpectedMonth);
}
DukValue expectedDay_get() const
{
if (gResearchProgressStage == RESEARCH_STAGE_INITIAL_RESEARCH || gResearchExpectedDay == 255)
return ToDuk(_context, nullptr);
return ToDuk(_context, gResearchExpectedDay);
}
DukValue lastResearchedItem_get() const
{
if (!gResearchLastItem)
return ToDuk(_context, nullptr);
return ToDuk(_context, *gResearchLastItem);
}
DukValue expectedItem_get() const
{
if (gResearchProgressStage == RESEARCH_STAGE_INITIAL_RESEARCH || !gResearchNextItem)
return ToDuk(_context, nullptr);
return ToDuk(_context, *gResearchNextItem);
}
std::vector<DukValue> inventedItems_get() const
{
std::vector<DukValue> result;
for (auto& item : gResearchItemsInvented)
{
result.push_back(ToDuk(_context, item));
}
return result;
}
void inventedItems_set(const std::vector<DukValue>& value)
{
auto list = ConvertResearchList(value);
gResearchItemsInvented = std::move(list);
ResearchFix();
}
std::vector<DukValue> uninventedItems_get() const
{
std::vector<DukValue> result;
for (auto& item : gResearchItemsUninvented)
{
result.push_back(ToDuk(_context, item));
}
return result;
}
void uninventedItems_set(const std::vector<DukValue>& value)
{
auto list = ConvertResearchList(value);
gResearchItemsUninvented = std::move(list);
ResearchFix();
}
bool isObjectResearched(const std::string& typez, ObjectEntryIndex index)
{
auto result = false;
auto type = ScObject::StringToObjectType(typez);
if (type)
{
result = ResearchIsInvented(*type, index);
}
else
{
duk_error(_context, DUK_ERR_ERROR, "Invalid object type.");
}
return result;
}
static void Register(duk_context* ctx)
{
dukglue_register_property(ctx, &ScResearch::funding_get, &ScResearch::funding_set, "funding");
dukglue_register_property(ctx, &ScResearch::priorities_get, &ScResearch::priorities_set, "priorities");
dukglue_register_property(ctx, &ScResearch::stage_get, &ScResearch::stage_set, "stage");
dukglue_register_property(ctx, &ScResearch::progress_get, &ScResearch::progress_set, "progress");
dukglue_register_property(ctx, &ScResearch::expectedMonth_get, nullptr, "expectedMonth");
dukglue_register_property(ctx, &ScResearch::expectedDay_get, nullptr, "expectedDay");
dukglue_register_property(ctx, &ScResearch::lastResearchedItem_get, nullptr, "lastResearchedItem");
dukglue_register_property(ctx, &ScResearch::expectedItem_get, nullptr, "expectedItem");
dukglue_register_property(ctx, &ScResearch::inventedItems_get, &ScResearch::inventedItems_set, "inventedItems");
dukglue_register_property(
ctx, &ScResearch::uninventedItems_get, &ScResearch::uninventedItems_set, "uninventedItems");
dukglue_register_method(ctx, &ScResearch::isObjectResearched, "isObjectResearched");
}
static std::vector<ResearchItem> ConvertResearchList(const std::vector<DukValue>& value)
{
auto& objManager = GetContext()->GetObjectManager();
std::vector<ResearchItem> result;
for (auto& item : value)
{
auto researchItem = FromDuk<ResearchItem>(item);
researchItem.flags = 0;
if (researchItem.type == Research::EntryType::Ride)
{
auto rideEntry = GetRideEntryByIndex(researchItem.entryIndex);
if (rideEntry != nullptr)
{
researchItem.category = GetRideTypeDescriptor(researchItem.baseRideType).GetResearchCategory();
result.push_back(researchItem);
}
}
else
{
auto sceneryGroup = objManager.GetLoadedObject(ObjectType::SceneryGroup, researchItem.entryIndex);
if (sceneryGroup != nullptr)
{
researchItem.baseRideType = 0;
researchItem.category = ResearchCategory::SceneryGroup;
result.push_back(researchItem);
}
}
}
return result;
}
};
} // namespace OpenRCT2::Scripting
#endif