-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Game Actions
Game Actions are the replacement for Game Commands. Each game command has its own class. The action can then be issued by using either an Execute or a Query.
auto gameAction = PlaceParkEntranceAction(x, y, z, direction);
auto result = GameActions::Execute(&gameAction);
auto gameAction = PlaceParkEntranceAction(x, y, z, direction);
auto result = GameActions::Query(&gameAction);
The result of an Execute or Query is the same a GameActionResult::Ptr
. This carries information such as success : Error
, cost : Cost
, location : Position
.
When you call GameActions::Execute
it works the same as a game command it will first Query the GameAction to confirm that it is possible before calling the Execute.
Flags can be passed into the GameAction with the SetFlags
command. This is used to pass in all of the GAME_COMMAND_FLAG_...
s apart from APPLY
. APPLY
is now handled by calling either the Execute or Query functions.
When a GameAction (or Game Command) needs to call into a GameAction this is called nesting. Instead of using the standard calls you need to use QueryNested
or ExecuteNested
. This is to prevent multiple game actions being issued on the network. A nested call will only be executed locally.
Below is an example GameAction class.
DEFINE_GAME_ACTION(ParkSetLoanAction, GAME_COMMAND_SET_CURRENT_LOAN, GameActionResult)
{
All GameActions use the DEFINE_GAME_ACTION
macro to setup the class. The first parameter is the class name. The second is the GAME_COMMAND. The GAME_COMMAND is used to serialise and deserialise when sending over the network. The final parameter is the return class of the two main functions (Query and Execute). GameActionResult can be subclassed to return additional information.
private:
money32 _value;
All of the parameters for calling this GameAction have an internal class member.
public:
ParkSetLoanAction()
{
}
ParkSetLoanAction(money32 value)
: _value(value)
{
}
A default constructor is required as networked versions of the GameAction pass in information using the Serialise function.
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags() | GA_FLAGS::ALLOW_WHILE_PAUSED;
}
To prevent duplicate code some flags such as ALLOW_WHILE_PAUSED
and EDITOR_ONLY
can be set here. This allows for much simpler structure of the Query and Execute.
void Serialise(DataSerialiser & stream) override
{
GameAction::Serialise(stream);
stream << DS_TAG(_value);
}
When networking a game action the Serialise function is used to get information into and out of the function. The values passed into the serialising stream must be class members. All parameters must be accounted for in this otherwise information will be lost during Multiplayer.
GameActionResult::Ptr Query() const override
{
The Query overrides the base GameAction. The Query must always be overridden.
auto currentLoan = gBankLoan;
auto loanDifference = currentLoan - _value;
if (_value > currentLoan)
{
if (_value > gMaxBankLoan)
{
return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_BORROW_ANY_MORE_MONEY, STR_BANK_REFUSES_TO_INCREASE_LOAN);
}
To return a result either the MakeResult
function or std::make_unique<GameActionResult>
must be used. The GA_ERROR enum is used to indicate success or failure. There are then 1 or 2 strings that will be used to generate the Error message. These replace gGameCommandErrorText. Finally you can optionally have a parameter for format args.
}
else
{
if (loanDifference > gCash)
{
return MakeResult(GA_ERROR::INSUFFICIENT_FUNDS, STR_CANT_PAY_BACK_LOAN, STR_NOT_ENOUGH_CASH_AVAILABLE);
}
}
return MakeResult();
}
MakeResult()
returns a default constructed result. This will be GA_ERROR::OK.
GameActionResult::Ptr Execute() const
{
gCash -= (gBankLoan - _value);
gBankLoan = _value;
The Execute function does not need to consider the validity of the parameters that has already been checked in the Query. Instead it needs to only modify the state.
auto windowManager = OpenRCT2::GetContext()->GetUiContext()->GetWindowManager();
windowManager->BroadcastIntent(Intent(INTENT_ACTION_UPDATE_CASH));
return MakeResult();
}
};
- Home
- FAQ & Common Issues
- Roadmap
- Installation
- Building
- Features
- Development
- Benchmarking & stress testing OpenRCT2
- Coding Style
- Commit Messages
- Overall program structure
- Data Structures
- CSS1.DAT
- Custom Music and Ride Music Objects
- Game Actions
- G1 Elements Layout
- game.cfg structure
- Maps
- Music Cleanup
- Objects
- Official extended scenery set
- Peep AI
- Peep Sprite Type
- RCT1 ride and vehicle types and their RCT2 equivalents
- RCT12_MAX_SOMETHING versus MAX_SOMETHING
- Ride rating calculation
- SV6 Ride Structure
- Settings in config.ini
- Sizes and angles in the game world
- Sprite List csg1.dat
- Sprite List g1.dat
- Strings used in RCT1
- Strings used in the game
- TD6 format
- Terminology
- Track Data
- Track Designs
- Track drawers, RTDs and vehicle types
- Track types
- Vehicle Sprite Layout
- Widget colours
- Debugging OpenRCT2 on macOS
- OpenGL renderer
- Rebase and Sync fork with OpenRCT2
- Release Checklist
- Replay System
- Using minidumps from crash reports
- Using Track Block Get Previous
- History
- Testing