Add plugin API for scenario and objective

This commit is contained in:
Ted John 2020-09-08 01:08:21 +01:00
parent e5107141e9
commit 146a754001
5 changed files with 421 additions and 0 deletions

View File

@ -1,6 +1,7 @@
0.3.0+ (in development)
------------------------------------------------------------------------
- Feature: [#10807] Add 2x and 4x zoom levels (currently limited to OpenGL).
- Feature: [#12703] Add scenario plugin APIs.
- Feature: [#12708] Add plugin-accessible names to all game actions.
- Feature: [#12712] Add TCP / socket plugin APIs.
- Feature: [#12840] Add Park.entranceFee to the plugin API.

View File

@ -35,6 +35,8 @@ declare global {
var network: Network;
/** APIs for the park and management of it. */
var park: Park;
/** APIs for the current scenario. */
var scenario: Scenario;
/**
* APIs for controlling the user interface.
* These will only be available to servers and clients that are not running headless mode.
@ -1470,6 +1472,109 @@ declare global {
postMessage(message: ParkMessageDesc): void;
}
type ScenarioObjectiveType =
"none" |
"guestsBy" |
"parkValueBy" |
"haveFun" |
"buildTheBest" |
"10Rollercoasters" |
"guestsAndRating" |
"monthlyRideIncome" |
"10RollercoastersLength" |
"finish5Rollercoasters" |
"replayLoanAndParkValue" |
"monthlyFoodIncome";
interface ScenarioObjective {
/**
* The objective type.
*/
type: ScenarioObjective;
/**
* The required number of guests.
*/
guests: number;
/**
* The year the objective must be completed by the end of.
*/
year: number;
/**
* The minimum length required for each rollercoaster.
*/
length: number;
/**
* The minimum excitement rating required for each rollercoaster.
*/
excitement: number;
/**
* The minimum park value required.
*/
parkValue: number;
/**
* The minimum monthly income from rides / food.
*/
monthlyIncome: number;
}
type ScenarioStatus = "inProgress" | "completed" | "failed";
interface Scenario {
/**
* The name of the scenario. This is not necessarily the name of the park.
*/
name: string;
/**
* The description of the scenario, shown above the scenario objective.
*/
details: string;
/**
* The entered player name if the scenario is complete.
*/
completedBy: string;
/**
* The filename of the scenario that is being played. Used to match the
* completion score with the scenario file.
*/
filename: string;
/**
* The criteria required to complete the scenario.
*/
objective: ScenarioObjective;
/**
* The number of consecutive days the park rating has been under the threshold for.
* This is reset when the park rating rises above the threshold again.
* Also used to post warning messages.
*/
parkRatingWarningDays: number;
/**
* The company value when the scenario was completed.
*/
completedCompanyValue?: number;
/**
* The current status of the scenario.
*/
status: ScenarioStatus;
/**
* The current highest recorded company value.
*/
companyValueRecord: number;
}
interface Cheats {
allowArbitraryRideTypeChanges: boolean;
allowTrackPlaceInvalidHeights: boolean;

View File

@ -410,6 +410,7 @@
<ClInclude Include="scripting\ScPark.hpp" />
<ClInclude Include="scripting\ScRide.hpp" />
<ClInclude Include="scripting\ScriptEngine.h" />
<ClInclude Include="scripting\ScScenario.hpp" />
<ClInclude Include="scripting\ScSocket.hpp" />
<ClInclude Include="scripting\ScTile.hpp" />
<ClInclude Include="sprites.h" />

View File

@ -0,0 +1,310 @@
/*****************************************************************************
* 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.
*****************************************************************************/
#pragma once
#ifdef ENABLE_SCRIPTING
# include "../Context.h"
# include "../GameState.h"
# include "../common.h"
# include "../core/String.hpp"
# include "../scenario/Scenario.h"
# include "../world/Park.h"
# include "Duktape.hpp"
# include "ScriptEngine.h"
# include <algorithm>
namespace OpenRCT2::Scripting
{
static const DukEnumMap<uint32_t> ScenarioObjectiveTypeMap({
{ "none", OBJECTIVE_NONE },
{ "guestsBy", OBJECTIVE_GUESTS_BY },
{ "parkValueBy", OBJECTIVE_PARK_VALUE_BY },
{ "haveFun", OBJECTIVE_HAVE_FUN },
{ "buildTheBest", OBJECTIVE_BUILD_THE_BEST },
{ "10Rollercoasters", OBJECTIVE_10_ROLLERCOASTERS },
{ "guestsAndRating", OBJECTIVE_GUESTS_AND_RATING },
{ "monthlyRideIncome", OBJECTIVE_MONTHLY_RIDE_INCOME },
{ "10RollercoastersLength", OBJECTIVE_10_ROLLERCOASTERS_LENGTH },
{ "finish5Rollercoasters", OBJECTIVE_FINISH_5_ROLLERCOASTERS },
{ "replayLoanAndParkValue", OBJECTIVE_REPLAY_LOAN_AND_PARK_VALUE },
{ "monthlyFoodIncome", OBJECTIVE_MONTHLY_FOOD_INCOME },
});
class ScScenarioObjective
{
private:
std::string type_get()
{
return std::string(ScenarioObjectiveTypeMap[gScenarioObjective.Type]);
}
void type_set(const std::string& value)
{
ThrowIfGameStateNotMutable();
gScenarioObjective.Type = ScenarioObjectiveTypeMap[value];
}
uint16_t guests_get()
{
if (gScenarioObjective.Type == OBJECTIVE_GUESTS_BY || gScenarioObjective.Type == OBJECTIVE_GUESTS_AND_RATING)
{
return gScenarioObjective.NumGuests;
}
return 0;
}
void guests_set(uint16_t value)
{
ThrowIfGameStateNotMutable();
if (gScenarioObjective.Type == OBJECTIVE_GUESTS_BY || gScenarioObjective.Type == OBJECTIVE_GUESTS_AND_RATING)
{
gScenarioObjective.NumGuests = value;
}
}
uint8_t year_get()
{
if (gScenarioObjective.Type == OBJECTIVE_GUESTS_BY || gScenarioObjective.Type == OBJECTIVE_PARK_VALUE_BY)
{
return gScenarioObjective.Year;
}
return 0;
}
void year_set(uint8_t value)
{
ThrowIfGameStateNotMutable();
if (gScenarioObjective.Type == OBJECTIVE_GUESTS_BY || gScenarioObjective.Type == OBJECTIVE_PARK_VALUE_BY)
{
gScenarioObjective.Year = value;
}
}
uint16_t length_get()
{
if (gScenarioObjective.Type == OBJECTIVE_10_ROLLERCOASTERS_LENGTH)
{
return gScenarioObjective.NumGuests;
}
return 0;
}
void length_set(uint16_t value)
{
ThrowIfGameStateNotMutable();
if (gScenarioObjective.Type == OBJECTIVE_10_ROLLERCOASTERS_LENGTH)
{
gScenarioObjective.NumGuests = value;
}
}
money32 excitement_get()
{
if (gScenarioObjective.Type == OBJECTIVE_FINISH_5_ROLLERCOASTERS)
{
return gScenarioObjective.Currency;
}
return 0;
}
void excitement_set(money32 value)
{
ThrowIfGameStateNotMutable();
if (gScenarioObjective.Type == OBJECTIVE_FINISH_5_ROLLERCOASTERS)
{
gScenarioObjective.Currency = value;
}
}
money32 parkValue_get()
{
if (gScenarioObjective.Type == OBJECTIVE_PARK_VALUE_BY
|| gScenarioObjective.Type == OBJECTIVE_REPLAY_LOAN_AND_PARK_VALUE)
{
return gScenarioObjective.Currency;
}
return 0;
}
void parkValue_set(money32 value)
{
ThrowIfGameStateNotMutable();
if (gScenarioObjective.Type == OBJECTIVE_PARK_VALUE_BY
|| gScenarioObjective.Type == OBJECTIVE_REPLAY_LOAN_AND_PARK_VALUE)
{
gScenarioObjective.Currency = value;
}
}
money32 monthlyIncome_get()
{
if (gScenarioObjective.Type == OBJECTIVE_MONTHLY_RIDE_INCOME
|| gScenarioObjective.Type == OBJECTIVE_MONTHLY_FOOD_INCOME)
{
return gScenarioObjective.Currency;
}
return 0;
}
void monthlyIncome_set(money32 value)
{
ThrowIfGameStateNotMutable();
if (gScenarioObjective.Type == OBJECTIVE_PARK_VALUE_BY
|| gScenarioObjective.Type == OBJECTIVE_REPLAY_LOAN_AND_PARK_VALUE)
{
gScenarioObjective.Currency = value;
}
}
public:
static void Register(duk_context* ctx)
{
dukglue_register_property(ctx, &ScScenarioObjective::type_get, &ScScenarioObjective::type_set, "type");
dukglue_register_property(ctx, &ScScenarioObjective::guests_get, &ScScenarioObjective::guests_set, "guests");
dukglue_register_property(ctx, &ScScenarioObjective::year_get, &ScScenarioObjective::year_set, "year");
dukglue_register_property(
ctx, &ScScenarioObjective::excitement_get, &ScScenarioObjective::excitement_set, "excitement");
dukglue_register_property(
ctx, &ScScenarioObjective::monthlyIncome_get, &ScScenarioObjective::monthlyIncome_set, "monthlyIncome");
dukglue_register_property(
ctx, &ScScenarioObjective::parkValue_get, &ScScenarioObjective::parkValue_set, "parkValue");
}
};
class ScScenario
{
public:
std::string name_get()
{
return gScenarioName;
}
void name_set(const std::string& value)
{
ThrowIfGameStateNotMutable();
gScenarioName = value;
}
std::string details_get()
{
return gScenarioDetails;
}
void details_set(const std::string& value)
{
ThrowIfGameStateNotMutable();
gScenarioDetails = value;
}
std::string completedBy_get()
{
return gScenarioCompletedBy;
}
void completedBy_set(const std::string& value)
{
ThrowIfGameStateNotMutable();
gScenarioCompletedBy = value;
}
std::string filename_get()
{
return gScenarioFileName;
}
void filename_set(const std::string& value)
{
ThrowIfGameStateNotMutable();
String::Set(gScenarioFileName, std::size(gScenarioFileName), value.c_str());
}
std::shared_ptr<ScScenarioObjective> objective_get() const
{
return std::make_shared<ScScenarioObjective>();
}
uint16_t parkRatingWarningDays_get() const
{
return gScenarioParkRatingWarningDays;
}
void parkRatingWarningDays_set(uint16_t value)
{
ThrowIfGameStateNotMutable();
gScenarioParkRatingWarningDays = value;
}
DukValue completedCompanyValue_get() const
{
auto ctx = GetContext()->GetScriptEngine().GetContext();
if (gScenarioCompletedCompanyValue == MONEY32_UNDEFINED
|| gScenarioCompletedCompanyValue == COMPANY_VALUE_ON_FAILED_OBJECTIVE)
{
return ToDuk(ctx, nullptr);
}
return ToDuk(ctx, gScenarioCompletedCompanyValue);
}
void completedCompanyValue_set(int32_t value)
{
ThrowIfGameStateNotMutable();
gScenarioCompletedCompanyValue = value;
}
std::string status_get() const
{
if (gScenarioCompletedCompanyValue == MONEY32_UNDEFINED)
return "inProgress";
else if (gScenarioCompletedCompanyValue == COMPANY_VALUE_ON_FAILED_OBJECTIVE)
return "failed";
return "completed";
}
void status_set(const std::string& value)
{
ThrowIfGameStateNotMutable();
if (value == "inProgress")
gScenarioCompletedCompanyValue = MONEY32_UNDEFINED;
else if (value == "failed")
gScenarioCompletedCompanyValue = COMPANY_VALUE_ON_FAILED_OBJECTIVE;
else if (value == "completed")
gScenarioCompletedCompanyValue = gCompanyValue;
}
money32 companyValueRecord_get() const
{
return gScenarioCompanyValueRecord;
}
void companyValueRecord_set(money32 value)
{
ThrowIfGameStateNotMutable();
gScenarioCompanyValueRecord = value;
}
public:
static void Register(duk_context* ctx)
{
dukglue_register_property(ctx, &ScScenario::name_get, &ScScenario::name_set, "name");
dukglue_register_property(ctx, &ScScenario::details_get, &ScScenario::details_set, "details");
dukglue_register_property(ctx, &ScScenario::completedBy_get, &ScScenario::completedBy_set, "completedBy");
dukglue_register_property(ctx, &ScScenario::filename_get, &ScScenario::filename_set, "filename");
dukglue_register_property(
ctx, &ScScenario::parkRatingWarningDays_get, &ScScenario::parkRatingWarningDays_set, "parkRatingWarningDays");
dukglue_register_property(ctx, &ScScenario::objective_get, nullptr, "objective");
dukglue_register_property(ctx, &ScScenario::status_get, &ScScenario::status_set, "status");
dukglue_register_property(
ctx, &ScScenario::completedCompanyValue_get, &ScScenario::completedCompanyValue_set, "completedCompanyValue");
dukglue_register_property(
ctx, &ScScenario::companyValueRecord_get, &ScScenario::companyValueRecord_set, "companyValueRecord");
}
};
} // namespace OpenRCT2::Scripting
#endif

View File

@ -33,6 +33,7 @@
# include "ScObject.hpp"
# include "ScPark.hpp"
# include "ScRide.hpp"
# include "ScScenario.hpp"
# include "ScSocket.hpp"
# include "ScTile.hpp"
@ -398,6 +399,8 @@ void ScriptEngine::Initialise()
ScSocket::Register(ctx);
ScListener::Register(ctx);
# endif
ScScenario::Register(ctx);
ScScenarioObjective::Register(ctx);
ScStaff::Register(ctx);
dukglue_register_global(ctx, std::make_shared<ScCheats>(), "cheats");
@ -407,6 +410,7 @@ void ScriptEngine::Initialise()
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<ScScenario>(), "scenario");
_initialised = true;
_pluginsLoaded = false;