OpenRCT2/src/openrct2/interface/InteractiveConsole.cpp

2206 lines
83 KiB
C++
Raw Normal View History

/*****************************************************************************
* Copyright (c) 2014-2024 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.
*****************************************************************************/
2018-06-22 22:59:46 +02:00
#include "InteractiveConsole.h"
2017-03-25 18:42:14 +01:00
#include "../Context.h"
#include "../Date.h"
2018-06-22 22:59:46 +02:00
#include "../EditorObjectSelectionSession.h"
#include "../Game.h"
#include "../GameState.h"
2018-06-22 22:59:46 +02:00
#include "../OpenRCT2.h"
2020-03-31 23:12:35 +02:00
#include "../PlatformEnvironment.h"
#include "../ReplayManager.h"
2018-06-22 22:59:46 +02:00
#include "../Version.h"
#include "../actions/CheatSetAction.h"
Split actions hpp files into separate h and cpp files (#13548) * Split up SmallSceneryPlace/Remove Added undo function for Remove Scenery * Refactor: Balloon and Banner actions hpp=>h/cpp * Refactor: rename all action *.hpp files to *.cpp This is preparation for separation in later commits. Note that without the complete set of commits in this branch, the code will not build. * Refactor Clear, Climate, Custom, and Footpath actions hpp=>h/cpp * VSCode: add src subdirectories to includePath * Refactor Guest actions hpp=>h/cpp * Refactor Land actions hpp=>h/cpp * Refactor LargeScenery actions hpp=>h/cpp * Refactor Load, Maze, Network actions hpp=>h/cpp * Refactor Park actions hpp=>h/cpp * Refactor/style: move private function declarations in actions *.h Previous action .h files included private function declarations with private member variables, before public function declarations. This commit re-orders the header files to the following order: - public member variables - private member variables - public functions - private functions * Refactor Pause action hpp=>h/cpp * Refactor Peep, Place, Player actions hpp=>h/cpp * Refactor Ride actions hpp=>h/cpp * Refactor Scenario, Set*, Sign* actions hpp=>h/cpp * Refactor SmallScenerySetColourAction hpp=>h/cpp * Refactor Staff actions hpp=>h/cpp * Refactor Surface, Tile, Track* actions hpp=>h/cpp * Refactor Wall and Water actions hpp=>h/cpp * Fix various includes and other compile errors Update includes for tests. Move static function declarations to .h files Add explicit includes to various files that were previously implicit (the required header was a nested include in an action hpp file, and the action .h file does not include that header) Move RideSetStatus string enum to the cpp file to avoid unused imports * Xcode: modify project file for actions refactor * Cleanup whitespace and end-of-file newlines Co-authored-by: duncanspumpkin <duncans_pumpkin@hotmail.co.uk>
2020-12-10 07:39:10 +01:00
#include "../actions/ClimateSetAction.h"
#include "../actions/ParkSetDateAction.h"
#include "../actions/ParkSetParameterAction.h"
#include "../actions/RideFreezeRatingAction.h"
Split actions hpp files into separate h and cpp files (#13548) * Split up SmallSceneryPlace/Remove Added undo function for Remove Scenery * Refactor: Balloon and Banner actions hpp=>h/cpp * Refactor: rename all action *.hpp files to *.cpp This is preparation for separation in later commits. Note that without the complete set of commits in this branch, the code will not build. * Refactor Clear, Climate, Custom, and Footpath actions hpp=>h/cpp * VSCode: add src subdirectories to includePath * Refactor Guest actions hpp=>h/cpp * Refactor Land actions hpp=>h/cpp * Refactor LargeScenery actions hpp=>h/cpp * Refactor Load, Maze, Network actions hpp=>h/cpp * Refactor Park actions hpp=>h/cpp * Refactor/style: move private function declarations in actions *.h Previous action .h files included private function declarations with private member variables, before public function declarations. This commit re-orders the header files to the following order: - public member variables - private member variables - public functions - private functions * Refactor Pause action hpp=>h/cpp * Refactor Peep, Place, Player actions hpp=>h/cpp * Refactor Ride actions hpp=>h/cpp * Refactor Scenario, Set*, Sign* actions hpp=>h/cpp * Refactor SmallScenerySetColourAction hpp=>h/cpp * Refactor Staff actions hpp=>h/cpp * Refactor Surface, Tile, Track* actions hpp=>h/cpp * Refactor Wall and Water actions hpp=>h/cpp * Fix various includes and other compile errors Update includes for tests. Move static function declarations to .h files Add explicit includes to various files that were previously implicit (the required header was a nested include in an action hpp file, and the action .h file does not include that header) Move RideSetStatus string enum to the cpp file to avoid unused imports * Xcode: modify project file for actions refactor * Cleanup whitespace and end-of-file newlines Co-authored-by: duncanspumpkin <duncans_pumpkin@hotmail.co.uk>
2020-12-10 07:39:10 +01:00
#include "../actions/RideSetPriceAction.h"
#include "../actions/RideSetSettingAction.h"
#include "../actions/ScenarioSetSettingAction.h"
Split actions hpp files into separate h and cpp files (#13548) * Split up SmallSceneryPlace/Remove Added undo function for Remove Scenery * Refactor: Balloon and Banner actions hpp=>h/cpp * Refactor: rename all action *.hpp files to *.cpp This is preparation for separation in later commits. Note that without the complete set of commits in this branch, the code will not build. * Refactor Clear, Climate, Custom, and Footpath actions hpp=>h/cpp * VSCode: add src subdirectories to includePath * Refactor Guest actions hpp=>h/cpp * Refactor Land actions hpp=>h/cpp * Refactor LargeScenery actions hpp=>h/cpp * Refactor Load, Maze, Network actions hpp=>h/cpp * Refactor Park actions hpp=>h/cpp * Refactor/style: move private function declarations in actions *.h Previous action .h files included private function declarations with private member variables, before public function declarations. This commit re-orders the header files to the following order: - public member variables - private member variables - public functions - private functions * Refactor Pause action hpp=>h/cpp * Refactor Peep, Place, Player actions hpp=>h/cpp * Refactor Ride actions hpp=>h/cpp * Refactor Scenario, Set*, Sign* actions hpp=>h/cpp * Refactor SmallScenerySetColourAction hpp=>h/cpp * Refactor Staff actions hpp=>h/cpp * Refactor Surface, Tile, Track* actions hpp=>h/cpp * Refactor Wall and Water actions hpp=>h/cpp * Fix various includes and other compile errors Update includes for tests. Move static function declarations to .h files Add explicit includes to various files that were previously implicit (the required header was a nested include in an action hpp file, and the action .h file does not include that header) Move RideSetStatus string enum to the cpp file to avoid unused imports * Xcode: modify project file for actions refactor * Cleanup whitespace and end-of-file newlines Co-authored-by: duncanspumpkin <duncans_pumpkin@hotmail.co.uk>
2020-12-10 07:39:10 +01:00
#include "../actions/StaffSetCostumeAction.h"
2018-06-22 22:59:46 +02:00
#include "../config/Config.h"
#include "../core/Console.hpp"
#include "../core/Guard.hpp"
2020-03-31 23:12:35 +02:00
#include "../core/Path.hpp"
#include "../core/String.hpp"
2018-01-05 22:17:33 +01:00
#include "../drawing/Drawing.h"
2018-01-06 18:32:25 +01:00
#include "../drawing/Font.h"
2021-12-07 21:55:27 +01:00
#include "../drawing/Image.h"
#include "../entity/Balloon.h"
2021-11-24 15:48:33 +01:00
#include "../entity/EntityList.h"
2021-11-24 15:58:01 +01:00
#include "../entity/EntityRegistry.h"
2021-11-25 22:47:24 +01:00
#include "../entity/Staff.h"
#include "../interface/Chat.h"
#include "../interface/Colour.h"
#include "../interface/Window_internal.h"
#include "../localisation/Formatting.h"
2018-01-06 18:32:25 +01:00
#include "../localisation/Localisation.h"
2017-10-06 22:37:06 +02:00
#include "../management/Finance.h"
#include "../management/NewsItem.h"
#include "../management/Research.h"
#include "../network/network.h"
2018-01-02 20:36:42 +01:00
#include "../object/Object.h"
2018-02-08 20:44:24 +01:00
#include "../object/ObjectList.h"
2016-07-05 20:33:12 +02:00
#include "../object/ObjectManager.h"
2016-07-02 22:52:17 +02:00
#include "../object/ObjectRepository.h"
#include "../platform/Platform.h"
#include "../profiling/Profiling.h"
2017-12-31 13:21:34 +01:00
#include "../ride/Ride.h"
2018-01-10 00:00:09 +01:00
#include "../ride/RideData.h"
#include "../ride/Vehicle.h"
2017-12-13 13:02:24 +01:00
#include "../util/Util.h"
#include "../windows/Intent.h"
2017-03-11 12:24:18 +01:00
#include "../world/Climate.h"
2017-12-31 21:40:00 +01:00
#include "../world/Park.h"
2018-01-11 10:59:26 +01:00
#include "../world/Scenery.h"
2018-01-06 00:45:53 +01:00
#include "Viewport.h"
2015-05-19 04:53:37 +02:00
2018-06-22 22:59:46 +02:00
#include <algorithm>
#include <array>
2018-06-22 22:59:46 +02:00
#include <cmath>
#include <cstdarg>
#include <cstdlib>
2018-06-22 22:59:46 +02:00
#include <deque>
#include <exception>
2018-06-22 22:59:46 +02:00
#include <string>
#include <thread>
#include <vector>
2018-06-22 22:59:46 +02:00
#ifndef NO_TTF
2018-07-21 16:17:06 +02:00
# include "../drawing/TTF.h"
#endif
using namespace OpenRCT2;
using arguments_t = std::vector<std::string>;
using OpenRCT2::Date;
static constexpr const char* ClimateNames[] = {
"cool_and_wet",
"warm",
"hot_and_dry",
"cold",
};
2023-01-16 21:14:50 +01:00
static int32_t ConsoleParseInt(const std::string& src, bool* valid);
static double ConsoleParseDouble(const std::string& src, bool* valid);
2015-05-19 04:53:37 +02:00
2023-01-16 21:14:50 +01:00
static void ConsoleWriteAllCommands(InteractiveConsole& console);
static int32_t ConsoleCommandVariables(InteractiveConsole& console, const arguments_t& argv);
static int32_t ConsoleCommandWindows(InteractiveConsole& console, const arguments_t& argv);
static int32_t ConsoleCommandHelp(InteractiveConsole& console, const arguments_t& argv);
2015-05-19 19:46:05 +02:00
2023-01-16 21:14:50 +01:00
static bool InvalidArguments(bool* invalid, bool arguments);
2015-06-19 16:51:54 +02:00
2018-06-22 22:59:46 +02:00
#define SET_FLAG(variable, flag, value) \
{ \
if (value) \
variable |= flag; \
else \
variable &= ~(flag); \
}
2023-01-16 21:14:50 +01:00
static int32_t ConsoleParseInt(const std::string& src, bool* valid)
2018-06-22 22:59:46 +02:00
{
utf8* end;
int32_t value;
value = static_cast<int32_t>(strtol(src.c_str(), &end, 10));
2018-06-22 22:59:46 +02:00
*valid = (*end == '\0');
return value;
}
2023-01-16 21:14:50 +01:00
static double ConsoleParseDouble(const std::string& src, bool* valid)
2018-06-22 22:59:46 +02:00
{
utf8* end;
double value;
value = strtod(src.c_str(), &end);
2018-06-22 22:59:46 +02:00
*valid = (*end == '\0');
return value;
}
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandClear(InteractiveConsole& console, [[maybe_unused]] const arguments_t& argv)
2015-05-19 04:53:37 +02:00
{
2018-03-11 20:46:03 +01:00
console.Clear();
return 0;
2015-05-19 04:53:37 +02:00
}
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandClose(InteractiveConsole& console, [[maybe_unused]] const arguments_t& argv)
{
2018-03-11 22:21:27 +01:00
console.Close();
return 0;
}
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandHide(InteractiveConsole& console, [[maybe_unused]] const arguments_t& argv)
2018-03-22 21:29:41 +01:00
{
console.Hide();
return 0;
}
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandEcho(InteractiveConsole& console, const arguments_t& argv)
2015-05-19 04:53:37 +02:00
{
if (!argv.empty())
2018-03-11 20:46:03 +01:00
console.WriteLine(argv[0]);
return 0;
2015-05-19 04:53:37 +02:00
}
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandRides(InteractiveConsole& console, const arguments_t& argv)
{
if (!argv.empty())
2018-06-22 22:59:46 +02:00
{
if (argv[0] == "list")
2018-06-22 22:59:46 +02:00
{
for (const auto& ride : GetRideManager())
2018-06-22 22:59:46 +02:00
{
auto name = ride.GetName();
2018-06-22 22:59:46 +02:00
console.WriteFormatLine(
"ride: %03d type: %02u subtype %03u operating mode: %02u name: %s", ride.id, ride.type, ride.subtype,
ride.mode, name.c_str());
}
2018-06-22 22:59:46 +02:00
}
else if (argv[0] == "set")
2018-06-22 22:59:46 +02:00
{
if (argv.size() < 4)
2018-06-22 22:59:46 +02:00
{
if (argv.size() > 1 && argv[1] == "mode")
2018-06-22 22:59:46 +02:00
{
2018-03-11 20:46:03 +01:00
console.WriteFormatLine("Ride modes are specified using integer IDs as given below:");
for (int32_t i = 0; i < static_cast<uint8_t>(RideMode::Count); i++)
2018-06-22 22:59:46 +02:00
{
char mode_name[128] = { 0 };
2022-07-31 14:22:58 +02:00
StringId mode_string_id = RideModeNames[i];
OpenRCT2::FormatStringLegacy(mode_name, 128, mode_string_id, nullptr);
2018-03-11 20:46:03 +01:00
console.WriteFormatLine("%02d - %s", i, mode_name);
}
2018-06-22 22:59:46 +02:00
}
else
{
2018-03-11 20:46:03 +01:00
console.WriteFormatLine("rides set type <ride id> <ride type>");
console.WriteFormatLine("rides set mode [<ride id> <operating mode>]");
console.WriteFormatLine("rides set mass <ride id> <mass value>");
console.WriteFormatLine("rides set excitement <ride id> <excitement value>");
console.WriteFormatLine("rides set intensity <ride id> <intensity value>");
console.WriteFormatLine("rides set nausea <ride id> <nausea value>");
2019-03-20 19:48:01 +01:00
console.WriteFormatLine("rides set price <ride id / all [type]> <price>");
}
return 0;
}
if (argv[1] == "type")
2018-06-22 22:59:46 +02:00
{
bool int_valid[2] = { false };
2023-01-16 21:14:50 +01:00
int32_t ride_index = ConsoleParseInt(argv[2], &int_valid[0]);
int32_t type = ConsoleParseInt(argv[3], &int_valid[1]);
2018-06-22 22:59:46 +02:00
if (!int_valid[0] || !int_valid[1])
{
2018-03-11 20:46:03 +01:00
console.WriteFormatLine("This command expects integer arguments");
2018-06-22 22:59:46 +02:00
}
else if (ride_index < 0)
{
2018-03-11 20:46:03 +01:00
console.WriteFormatLine("Ride index must not be negative");
2018-06-22 22:59:46 +02:00
}
else
{
auto res = SetOperatingSetting(RideId::FromUnderlying(ride_index), RideSetSetting::RideType, type);
if (res == kMoney64Undefined)
2018-06-22 22:59:46 +02:00
{
2024-03-03 22:44:15 +01:00
if (!GetGameState().Cheats.AllowArbitraryRideTypeChanges)
2021-05-11 13:27:39 +02:00
{
console.WriteFormatLine(
"That didn't work. Try enabling the 'Allow arbitrary ride type changes' cheat");
}
else
{
console.WriteFormatLine("That didn't work");
}
}
}
}
else if (argv[1] == "mode")
2018-06-22 22:59:46 +02:00
{
bool int_valid[2] = { false };
2023-01-16 21:14:50 +01:00
int32_t ride_index = ConsoleParseInt(argv[2], &int_valid[0]);
int32_t mode = ConsoleParseInt(argv[3], &int_valid[1]);
2018-06-22 22:59:46 +02:00
if (!int_valid[0] || !int_valid[1])
{
2018-03-11 20:46:03 +01:00
console.WriteFormatLine("This command expects integer arguments");
2018-06-22 22:59:46 +02:00
}
else if (ride_index < 0)
{
2018-03-11 20:46:03 +01:00
console.WriteFormatLine("Ride index must not be negative");
2018-06-22 22:59:46 +02:00
}
else
{
auto ride = GetRide(RideId::FromUnderlying(ride_index));
if (mode >= static_cast<uint8_t>(RideMode::Count))
2018-06-22 22:59:46 +02:00
{
2018-03-11 20:46:03 +01:00
console.WriteFormatLine("Invalid ride mode.");
}
else if (ride == nullptr)
2018-06-22 22:59:46 +02:00
{
2018-03-11 20:46:03 +01:00
console.WriteFormatLine("No ride found with index %d", ride_index);
}
2018-06-22 22:59:46 +02:00
else
{
ride->mode = static_cast<RideMode>(mode & 0xFF);
InvalidateTestResults(*ride);
}
}
}
else if (argv[1] == "mass")
2018-06-22 22:59:46 +02:00
{
bool int_valid[2] = { false };
2023-01-16 21:14:50 +01:00
int32_t ride_index = ConsoleParseInt(argv[2], &int_valid[0]);
int32_t mass = ConsoleParseInt(argv[3], &int_valid[1]);
2018-06-22 22:59:46 +02:00
if (ride_index < 0)
{
2018-03-11 20:46:03 +01:00
console.WriteFormatLine("Ride index must not be negative");
}
2018-06-22 22:59:46 +02:00
else if (!int_valid[0] || !int_valid[1])
{
2018-03-11 20:46:03 +01:00
console.WriteFormatLine("This command expects integer arguments");
}
2018-06-22 22:59:46 +02:00
else
{
auto ride = GetRide(RideId::FromUnderlying(ride_index));
2018-06-22 22:59:46 +02:00
if (mass <= 0)
{
2018-03-11 20:46:03 +01:00
console.WriteFormatLine("Friction value must be strictly positive");
}
else if (ride == nullptr)
2018-06-22 22:59:46 +02:00
{
2018-03-11 20:46:03 +01:00
console.WriteFormatLine("No ride found with index %d", ride_index);
}
2018-06-22 22:59:46 +02:00
else
{
for (int32_t i = 0; i < ride->NumTrains; ++i)
2018-06-22 22:59:46 +02:00
{
for (Vehicle* vehicle = GetEntity<Vehicle>(ride->vehicles[i]); vehicle != nullptr;
vehicle = GetEntity<Vehicle>(vehicle->next_vehicle_on_train))
2018-06-22 22:59:46 +02:00
{
2017-12-17 18:00:42 +01:00
vehicle->mass = mass;
}
}
}
}
}
else if (argv[1] == "excitement")
2018-06-22 22:59:46 +02:00
{
bool int_valid[2] = { false };
2023-01-16 21:14:50 +01:00
int32_t ride_index = ConsoleParseInt(argv[2], &int_valid[0]);
ride_rating excitement = ConsoleParseInt(argv[3], &int_valid[1]);
2018-06-22 22:59:46 +02:00
if (ride_index < 0)
{
2018-03-11 20:46:03 +01:00
console.WriteFormatLine("Ride index must not be negative");
}
2018-06-22 22:59:46 +02:00
else if (!int_valid[0] || !int_valid[1])
{
2018-03-11 20:46:03 +01:00
console.WriteFormatLine("This command expects integer arguments");
}
2018-06-22 22:59:46 +02:00
else
{
auto rideIndex = RideId::FromUnderlying(ride_index);
auto ride = GetRide(rideIndex);
2018-06-22 22:59:46 +02:00
if (excitement <= 0)
{
2018-03-11 20:46:03 +01:00
console.WriteFormatLine("Excitement value must be strictly positive");
}
else if (ride == nullptr)
2018-06-22 22:59:46 +02:00
{
2018-03-11 20:46:03 +01:00
console.WriteFormatLine("No ride found with index %d", ride_index);
}
2018-06-22 22:59:46 +02:00
else
{
auto rideAction = RideFreezeRatingAction(rideIndex, RideRatingType::Excitement, excitement);
GameActions::Execute(&rideAction);
}
}
}
else if (argv[1] == "intensity")
2018-06-22 22:59:46 +02:00
{
bool int_valid[2] = { false };
2023-01-16 21:14:50 +01:00
int32_t ride_index = ConsoleParseInt(argv[2], &int_valid[0]);
ride_rating intensity = ConsoleParseInt(argv[3], &int_valid[1]);
2018-06-22 22:59:46 +02:00
if (ride_index < 0)
{
2018-03-11 20:46:03 +01:00
console.WriteFormatLine("Ride index must not be negative");
}
2018-06-22 22:59:46 +02:00
else if (!int_valid[0] || !int_valid[1])
{
2018-03-11 20:46:03 +01:00
console.WriteFormatLine("This command expects integer arguments");
}
2018-06-22 22:59:46 +02:00
else
{
auto rideIndex = RideId::FromUnderlying(ride_index);
auto ride = GetRide(rideIndex);
2018-06-22 22:59:46 +02:00
if (intensity <= 0)
{
2018-03-11 20:46:03 +01:00
console.WriteFormatLine("Intensity value must be strictly positive");
}
else if (ride == nullptr)
2018-06-22 22:59:46 +02:00
{
2018-03-11 20:46:03 +01:00
console.WriteFormatLine("No ride found with index %d", ride_index);
}
2018-06-22 22:59:46 +02:00
else
{
auto rideAction = RideFreezeRatingAction(rideIndex, RideRatingType::Intensity, intensity);
GameActions::Execute(&rideAction);
}
}
}
else if (argv[1] == "nausea")
2018-06-22 22:59:46 +02:00
{
bool int_valid[2] = { false };
2023-01-16 21:14:50 +01:00
int32_t ride_index = ConsoleParseInt(argv[2], &int_valid[0]);
ride_rating nausea = ConsoleParseInt(argv[3], &int_valid[1]);
2018-06-22 22:59:46 +02:00
if (ride_index < 0)
{
2018-03-11 20:46:03 +01:00
console.WriteFormatLine("Ride index must not be negative");
}
2018-06-22 22:59:46 +02:00
else if (!int_valid[0] || !int_valid[1])
{
2018-03-11 20:46:03 +01:00
console.WriteFormatLine("This command expects integer arguments");
}
2018-06-22 22:59:46 +02:00
else
{
auto rideIndex = RideId::FromUnderlying(ride_index);
auto ride = GetRide(rideIndex);
2018-06-22 22:59:46 +02:00
if (nausea <= 0)
{
2018-03-11 20:46:03 +01:00
console.WriteFormatLine("Nausea value must be strictly positive");
}
else if (ride == nullptr)
2018-06-22 22:59:46 +02:00
{
2018-03-11 20:46:03 +01:00
console.WriteFormatLine("No ride found with index %d", ride_index);
}
2018-06-22 22:59:46 +02:00
else
{
auto rideAction = RideFreezeRatingAction(rideIndex, RideRatingType::Nausea, nausea);
GameActions::Execute(&rideAction);
}
}
}
else if (argv[1] == "price")
{
bool int_valid[2] = { false };
if (argv[2] == "all")
{
2023-01-16 21:14:50 +01:00
auto arg1 = ConsoleParseInt(argv[3], &int_valid[0]);
if (argv.size() <= 4)
{
auto price = arg1;
if (int_valid[0])
{
2019-08-04 17:00:15 +02:00
for (const auto& ride : GetRideManager())
{
2019-08-04 17:00:15 +02:00
auto rideSetPrice = RideSetPriceAction(ride.id, price, true);
GameActions::Execute(&rideSetPrice);
}
}
else
{
console.WriteFormatLine("This command expects one or two integer arguments");
}
}
else
{
auto rideType = arg1;
2023-01-16 21:14:50 +01:00
auto price = ConsoleParseInt(argv[4], &int_valid[1]);
if (int_valid[0] && int_valid[1])
{
for (const auto& ride : GetRideManager())
{
if (ride.type == rideType)
{
auto rideSetPrice = RideSetPriceAction(ride.id, price, true);
GameActions::Execute(&rideSetPrice);
}
}
}
else
{
console.WriteFormatLine("This command expects one or two integer arguments");
}
}
}
else
{
2023-01-16 21:14:50 +01:00
int32_t rideId = ConsoleParseInt(argv[2], &int_valid[0]);
money64 price = ConsoleParseInt(argv[3], &int_valid[1]);
if (!int_valid[0] || !int_valid[1])
{
console.WriteFormatLine("This command expects the string all or two integer arguments");
}
2019-03-18 22:50:29 +01:00
else
{
2022-01-19 15:54:57 +01:00
auto rideSetPrice = RideSetPriceAction(RideId::FromUnderlying(rideId), price, true);
2019-03-18 22:50:29 +01:00
GameActions::Execute(&rideSetPrice);
}
}
}
}
2018-06-22 22:59:46 +02:00
}
else
{
2018-03-11 20:46:03 +01:00
console.WriteFormatLine("subcommands: list, set");
}
return 0;
}
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandStaff(InteractiveConsole& console, const arguments_t& argv)
{
if (!argv.empty())
2018-06-22 22:59:46 +02:00
{
if (argv[0] == "list")
2018-06-22 22:59:46 +02:00
{
for (auto peep : EntityList<Staff>())
2018-06-22 22:59:46 +02:00
{
2019-07-21 21:44:19 +02:00
auto name = peep->GetName();
2018-06-22 22:59:46 +02:00
console.WriteFormatLine(
2023-01-27 18:18:44 +01:00
"staff id %03d type: %02u energy %03u name %s", peep->Id, peep->AssignedStaffType, peep->Energy,
name.c_str());
}
2018-06-22 22:59:46 +02:00
}
else if (argv[0] == "set")
2018-06-22 22:59:46 +02:00
{
if (argv.size() < 4)
2018-06-22 22:59:46 +02:00
{
2018-03-11 20:46:03 +01:00
console.WriteFormatLine("staff set energy <staff id> <value 0-255>");
console.WriteFormatLine("staff set costume <staff id> <costume id>");
for (int32_t i = 0; i < static_cast<uint8_t>(EntertainerCostume::Count); i++)
2018-06-22 22:59:46 +02:00
{
char costume_name[128] = { 0 };
2022-07-31 14:22:58 +02:00
StringId costume = StaffCostumeNames[i];
OpenRCT2::FormatStringLegacy(costume_name, 128, STR_DROPDOWN_MENU_LABEL, &costume);
// That's a terrible hack here. Costume names include inline sprites
// that don't work well with the console, so manually skip past them.
2018-03-11 20:46:03 +01:00
console.WriteFormatLine(" costume %i: %s", i, costume_name + 7);
}
return 0;
}
if (argv[1] == "energy")
2018-06-22 22:59:46 +02:00
{
int32_t int_val[3];
bool int_valid[3] = { false };
2023-01-16 21:14:50 +01:00
int_val[0] = ConsoleParseInt(argv[2], &int_valid[0]);
int_val[1] = ConsoleParseInt(argv[3], &int_valid[1]);
if (int_valid[0] && int_valid[1])
2018-06-22 22:59:46 +02:00
{
Peep* peep = GetEntity<Peep>(EntityId::FromUnderlying(int_val[0]));
if (peep != nullptr)
{
peep->Energy = int_val[1];
peep->EnergyTarget = int_val[1];
}
}
2018-06-22 22:59:46 +02:00
}
else if (argv[1] == "costume")
2018-06-22 22:59:46 +02:00
{
int32_t int_val[2];
bool int_valid[2] = { false };
2023-01-16 21:14:50 +01:00
int_val[0] = ConsoleParseInt(argv[2], &int_valid[0]);
int_val[1] = ConsoleParseInt(argv[3], &int_valid[1]);
2018-06-22 22:59:46 +02:00
if (!int_valid[0])
{
2018-03-11 20:46:03 +01:00
console.WriteLineError("Invalid staff ID");
return 1;
}
auto staff = GetEntity<Staff>(EntityId::FromUnderlying(int_val[0]));
if (staff == nullptr)
{
console.WriteLineError("Invalid staff ID");
return 1;
}
if (staff->AssignedStaffType != StaffType::Entertainer)
2018-06-22 22:59:46 +02:00
{
2018-03-11 20:46:03 +01:00
console.WriteLineError("Specified staff is not entertainer");
return 1;
}
if (!int_valid[1] || int_val[1] < 0 || int_val[1] >= static_cast<uint8_t>(EntertainerCostume::Count))
2018-06-22 22:59:46 +02:00
{
2018-03-11 20:46:03 +01:00
console.WriteLineError("Invalid costume ID");
return 1;
}
EntertainerCostume costume = static_cast<EntertainerCostume>(int_val[1]);
auto staffSetCostumeAction = StaffSetCostumeAction(EntityId::FromUnderlying(int_val[0]), costume);
GameActions::Execute(&staffSetCostumeAction);
}
}
2018-06-22 22:59:46 +02:00
}
else
{
2018-03-11 20:46:03 +01:00
console.WriteFormatLine("subcommands: list, set");
}
return 0;
}
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandGet(InteractiveConsole& console, const arguments_t& argv)
2015-05-19 04:53:37 +02:00
{
2024-01-21 22:05:22 +01:00
auto& gameState = GetGameState();
if (!argv.empty())
2018-06-22 22:59:46 +02:00
{
if (argv[0] == "park_rating")
2018-06-22 22:59:46 +02:00
{
console.WriteFormatLine("park_rating %d", gameState.Park.Rating);
}
else if (argv[0] == "park_value")
2018-06-22 22:59:46 +02:00
{
console.WriteFormatLine("park_value %d", gameState.Park.Value / 10);
}
else if (argv[0] == "company_value")
2018-06-22 22:59:46 +02:00
{
2024-02-27 16:31:38 +01:00
console.WriteFormatLine("company_value %d", gameState.CompanyValue / 10);
}
else if (argv[0] == "money")
2018-06-22 22:59:46 +02:00
{
console.WriteFormatLine("money %d.%d0", gameState.Cash / 10, gameState.Cash % 10);
}
else if (argv[0] == "scenario_initial_cash")
2018-06-22 22:59:46 +02:00
{
console.WriteFormatLine("scenario_initial_cash %d", gameState.InitialCash / 10);
}
else if (argv[0] == "current_loan")
2018-06-22 22:59:46 +02:00
{
console.WriteFormatLine("current_loan %d", gameState.BankLoan / 10);
}
else if (argv[0] == "max_loan")
2018-06-22 22:59:46 +02:00
{
2024-02-13 21:36:35 +01:00
console.WriteFormatLine("max_loan %d", gameState.MaxBankLoan / 10);
}
else if (argv[0] == "guest_initial_cash")
2018-06-22 22:59:46 +02:00
{
2024-01-22 23:28:16 +01:00
console.WriteFormatLine(
"guest_initial_cash %d.%d0", gameState.GuestInitialCash / 10, gameState.GuestInitialCash % 10);
}
else if (argv[0] == "guest_initial_happiness")
2018-06-22 22:59:46 +02:00
{
2024-01-22 23:28:16 +01:00
uint32_t current_happiness = gameState.GuestInitialHappiness;
2018-06-22 22:59:46 +02:00
for (int32_t i = 15; i <= 99; i++)
{
if (i == 99)
{
2024-01-22 23:28:16 +01:00
console.WriteFormatLine("guest_initial_happiness %d%% (%d)", 15, gameState.GuestInitialHappiness);
}
else if (current_happiness == Park::CalculateGuestInitialHappiness(i))
2018-06-22 22:59:46 +02:00
{
2024-01-22 23:28:16 +01:00
console.WriteFormatLine("guest_initial_happiness %d%% (%d)", i, gameState.GuestInitialHappiness);
break;
}
}
}
else if (argv[0] == "guest_initial_hunger")
2018-06-22 22:59:46 +02:00
{
console.WriteFormatLine(
2024-01-22 23:28:16 +01:00
"guest_initial_hunger %d%% (%d)", ((255 - gameState.GuestInitialHunger) * 100) / 255,
gameState.GuestInitialHunger);
}
else if (argv[0] == "guest_initial_thirst")
2018-06-22 22:59:46 +02:00
{
console.WriteFormatLine(
2024-01-22 23:28:16 +01:00
"guest_initial_thirst %d%% (%d)", ((255 - gameState.GuestInitialThirst) * 100) / 255,
gameState.GuestInitialThirst);
}
else if (argv[0] == "guest_prefer_less_intense_rides")
2018-06-22 22:59:46 +02:00
{
console.WriteFormatLine(
"guest_prefer_less_intense_rides %d", (gameState.Park.Flags & PARK_FLAGS_PREF_LESS_INTENSE_RIDES) != 0);
}
else if (argv[0] == "guest_prefer_more_intense_rides")
2018-06-22 22:59:46 +02:00
{
console.WriteFormatLine(
"guest_prefer_more_intense_rides %d", (gameState.Park.Flags & PARK_FLAGS_PREF_MORE_INTENSE_RIDES) != 0);
}
else if (argv[0] == "forbid_marketing_campaigns")
2018-06-22 22:59:46 +02:00
{
console.WriteFormatLine(
"forbid_marketing_campaigns %d", (gameState.Park.Flags & PARK_FLAGS_FORBID_MARKETING_CAMPAIGN) != 0);
}
else if (argv[0] == "forbid_landscape_changes")
2018-06-22 22:59:46 +02:00
{
console.WriteFormatLine(
"forbid_landscape_changes %d", (gameState.Park.Flags & PARK_FLAGS_FORBID_LANDSCAPE_CHANGES) != 0);
}
else if (argv[0] == "forbid_tree_removal")
2018-06-22 22:59:46 +02:00
{
console.WriteFormatLine("forbid_tree_removal %d", (gameState.Park.Flags & PARK_FLAGS_FORBID_TREE_REMOVAL) != 0);
}
else if (argv[0] == "forbid_high_construction")
2018-06-22 22:59:46 +02:00
{
console.WriteFormatLine(
"forbid_high_construction %d", (gameState.Park.Flags & PARK_FLAGS_FORBID_HIGH_CONSTRUCTION) != 0);
}
else if (argv[0] == "pay_for_rides")
2018-06-22 22:59:46 +02:00
{
console.WriteFormatLine("pay_for_rides %d", (gameState.Park.Flags & PARK_FLAGS_PARK_FREE_ENTRY) != 0);
}
else if (argv[0] == "no_money")
2018-06-22 22:59:46 +02:00
{
console.WriteFormatLine("no_money %d", (gameState.Park.Flags & PARK_FLAGS_NO_MONEY) != 0);
}
else if (argv[0] == "difficult_park_rating")
2018-06-22 22:59:46 +02:00
{
console.WriteFormatLine("difficult_park_rating %d", (gameState.Park.Flags & PARK_FLAGS_DIFFICULT_PARK_RATING) != 0);
}
else if (argv[0] == "difficult_guest_generation")
2018-06-22 22:59:46 +02:00
{
console.WriteFormatLine(
"difficult_guest_generation %d", (gameState.Park.Flags & PARK_FLAGS_DIFFICULT_GUEST_GENERATION) != 0);
}
else if (argv[0] == "park_open")
2018-06-22 22:59:46 +02:00
{
console.WriteFormatLine("park_open %d", (gameState.Park.Flags & PARK_FLAGS_PARK_OPEN) != 0);
}
else if (argv[0] == "land_rights_cost")
2018-06-22 22:59:46 +02:00
{
2024-03-07 19:44:21 +01:00
console.WriteFormatLine("land_rights_cost %d.%d0", gameState.LandPrice / 10, gameState.LandPrice % 10);
}
else if (argv[0] == "construction_rights_cost")
2018-06-22 22:59:46 +02:00
{
console.WriteFormatLine(
"construction_rights_cost %d.%d0", gameState.ConstructionRightsPrice / 10,
gameState.ConstructionRightsPrice % 10);
}
else if (argv[0] == "climate")
2018-06-22 22:59:46 +02:00
{
console.WriteFormatLine(
"climate %s (%d)", ClimateNames[EnumValue(gameState.Climate)], EnumValue(gameState.Climate));
}
else if (argv[0] == "game_speed")
2018-06-22 22:59:46 +02:00
{
2018-03-11 20:46:03 +01:00
console.WriteFormatLine("game_speed %d", gGameSpeed);
}
else if (argv[0] == "console_small_font")
2018-06-22 22:59:46 +02:00
{
console.WriteFormatLine("console_small_font %d", gConfigInterface.ConsoleSmallFont);
}
else if (argv[0] == "location")
2018-06-22 22:59:46 +02:00
{
WindowBase* w = WindowGetMain();
2018-06-22 22:59:46 +02:00
if (w != nullptr)
{
Viewport* viewport = WindowGetViewport(w);
2023-01-16 21:14:50 +01:00
auto info = GetMapCoordinatesFromPos(
{ viewport->view_width / 2, viewport->view_height / 2 }, EnumsToFlags(ViewportInteractionItem::Terrain));
2020-03-07 21:07:18 +01:00
auto tileMapCoord = TileCoordsXY(info.Loc);
2020-03-07 21:07:18 +01:00
console.WriteFormatLine("location %d %d", tileMapCoord.x, tileMapCoord.y);
}
}
else if (argv[0] == "window_scale")
2018-06-22 22:59:46 +02:00
{
console.WriteFormatLine("window_scale %.3f", gConfigGeneral.WindowScale);
}
else if (argv[0] == "window_limit")
2018-06-22 22:59:46 +02:00
{
console.WriteFormatLine("window_limit %d", gConfigGeneral.WindowLimit);
}
else if (argv[0] == "render_weather_effects")
2018-06-22 22:59:46 +02:00
{
console.WriteFormatLine("render_weather_effects %d", gConfigGeneral.RenderWeatherEffects);
}
else if (argv[0] == "render_weather_gloom")
2018-06-22 22:59:46 +02:00
{
console.WriteFormatLine("render_weather_gloom %d", gConfigGeneral.RenderWeatherGloom);
}
else if (argv[0] == "cheat_sandbox_mode")
2018-06-22 22:59:46 +02:00
{
2024-03-03 22:44:15 +01:00
console.WriteFormatLine("cheat_sandbox_mode %d", GetGameState().Cheats.SandboxMode);
}
else if (argv[0] == "cheat_disable_clearance_checks")
2018-06-22 22:59:46 +02:00
{
2024-03-03 22:44:15 +01:00
console.WriteFormatLine("cheat_disable_clearance_checks %d", GetGameState().Cheats.DisableClearanceChecks);
}
else if (argv[0] == "cheat_disable_support_limits")
2018-06-22 22:59:46 +02:00
{
2024-03-03 22:44:15 +01:00
console.WriteFormatLine("cheat_disable_support_limits %d", GetGameState().Cheats.DisableSupportLimits);
}
else if (argv[0] == "current_rotation")
{
2023-01-16 21:14:50 +01:00
console.WriteFormatLine("current_rotation %d", GetCurrentRotation());
}
else if (argv[0] == "host_timescale")
{
console.WriteFormatLine("host_timescale %.02f", OpenRCT2::GetContext()->GetTimeScale());
}
#ifndef NO_TTF
else if (argv[0] == "enable_hinting")
2018-06-22 22:59:46 +02:00
{
2022-10-16 17:55:49 +02:00
console.WriteFormatLine("enable_hinting %d", gConfigFonts.EnableHinting);
}
#endif
2018-06-22 22:59:46 +02:00
else
{
2018-03-11 20:46:03 +01:00
console.WriteLineWarning("Invalid variable.");
}
}
return 0;
}
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandSet(InteractiveConsole& console, const arguments_t& argv)
{
if (argv.size() > 1)
2018-06-22 22:59:46 +02:00
{
int32_t int_val[4];
bool int_valid[4];
double double_val[4];
bool double_valid[4];
bool invalidArgs = false;
for (uint32_t i = 0; i < std::size(int_val); i++)
2018-06-22 22:59:46 +02:00
{
if (i + 1 < argv.size())
2018-06-22 22:59:46 +02:00
{
2023-01-16 21:14:50 +01:00
int_val[i] = ConsoleParseInt(argv[i + 1], &int_valid[i]);
double_val[i] = ConsoleParseDouble(argv[i + 1], &double_valid[i]);
}
2018-06-22 22:59:46 +02:00
else
{
int_val[i] = 0;
int_valid[i] = false;
double_val[i] = 0;
double_valid[i] = false;
}
}
auto& gameState = GetGameState();
2023-01-16 21:14:50 +01:00
if (argv[0] == "money" && InvalidArguments(&invalidArgs, double_valid[0]))
2018-06-22 22:59:46 +02:00
{
money64 money = ToMoney64FromGBP(double_val[0]);
if (gameState.Cash != money)
2018-06-22 22:59:46 +02:00
{
auto cheatSetAction = CheatSetAction(CheatType::SetMoney, money);
cheatSetAction.SetCallback([&console](const GameAction*, const GameActions::Result* res) {
if (res->Error != GameActions::Status::Ok)
console.WriteLineError("set money command failed, likely due to permissions.");
else
console.Execute("get money");
});
GameActions::Execute(&cheatSetAction);
}
else
2018-06-22 22:59:46 +02:00
{
2018-03-11 20:46:03 +01:00
console.Execute("get money");
}
}
2023-01-16 21:14:50 +01:00
else if (argv[0] == "scenario_initial_cash" && InvalidArguments(&invalidArgs, int_valid[0]))
2018-06-22 22:59:46 +02:00
{
auto scenarioSetSetting = ScenarioSetSettingAction(
ScenarioSetSetting::InitialCash, std::clamp(ToMoney64FromGBP(int_val[0]), 0.00_GBP, 1000000.00_GBP));
scenarioSetSetting.SetCallback([&console](const GameAction*, const GameActions::Result* res) {
if (res->Error != GameActions::Status::Ok)
console.WriteLineError("set scenario_initial_cash command failed, likely due to permissions.");
else
console.Execute("get scenario_initial_cash");
});
GameActions::Execute(&scenarioSetSetting);
}
2023-01-16 21:14:50 +01:00
else if (argv[0] == "current_loan" && InvalidArguments(&invalidArgs, int_valid[0]))
2018-06-22 22:59:46 +02:00
{
auto amount = std::clamp(
2024-02-13 21:36:35 +01:00
ToMoney64FromGBP(int_val[0]) - ToMoney64FromGBP(int_val[0] % 1000), 0.00_GBP, gameState.MaxBankLoan);
auto scenarioSetSetting = ScenarioSetSettingAction(ScenarioSetSetting::InitialLoan, amount);
scenarioSetSetting.SetCallback([&console](const GameAction*, const GameActions::Result* res) {
if (res->Error != GameActions::Status::Ok)
console.WriteLineError("set current_loan command failed, likely due to permissions.");
else
console.Execute("get current_loan");
});
GameActions::Execute(&scenarioSetSetting);
}
2023-01-16 21:14:50 +01:00
else if (argv[0] == "max_loan" && InvalidArguments(&invalidArgs, int_valid[0]))
2018-06-22 22:59:46 +02:00
{
auto amount = std::clamp(
ToMoney64FromGBP(int_val[0]) - ToMoney64FromGBP(int_val[0] % 1000), 0.00_GBP, 5000000.00_GBP);
auto scenarioSetSetting = ScenarioSetSettingAction(ScenarioSetSetting::MaximumLoanSize, amount);
scenarioSetSetting.SetCallback([&console](const GameAction*, const GameActions::Result* res) {
if (res->Error != GameActions::Status::Ok)
console.WriteLineError("set max_loan command failed, likely due to permissions.");
else
console.Execute("get max_loan");
});
GameActions::Execute(&scenarioSetSetting);
}
2023-01-16 21:14:50 +01:00
else if (argv[0] == "guest_initial_cash" && InvalidArguments(&invalidArgs, double_valid[0]))
2018-06-22 22:59:46 +02:00
{
auto scenarioSetSetting = ScenarioSetSettingAction(
ScenarioSetSetting::AverageCashPerGuest, std::clamp(ToMoney64FromGBP(double_val[0]), 0.00_GBP, 1000.00_GBP));
scenarioSetSetting.SetCallback([&console](const GameAction*, const GameActions::Result* res) {
if (res->Error != GameActions::Status::Ok)
console.WriteLineError("set guest_initial_cash command failed, likely due to permissions.");
else
console.Execute("get guest_initial_cash");
});
GameActions::Execute(&scenarioSetSetting);
}
2023-01-16 21:14:50 +01:00
else if (argv[0] == "guest_initial_happiness" && InvalidArguments(&invalidArgs, int_valid[0]))
2018-06-22 22:59:46 +02:00
{
auto scenarioSetSetting = ScenarioSetSettingAction(
ScenarioSetSetting::GuestInitialHappiness,
Park::CalculateGuestInitialHappiness(static_cast<uint8_t>(int_val[0])));
scenarioSetSetting.SetCallback([&console](const GameAction*, const GameActions::Result* res) {
if (res->Error != GameActions::Status::Ok)
console.WriteLineError("set guest_initial_happiness command failed, likely due to permissions.");
else
console.Execute("get guest_initial_happiness");
});
GameActions::Execute(&scenarioSetSetting);
}
2023-01-16 21:14:50 +01:00
else if (argv[0] == "guest_initial_hunger" && InvalidArguments(&invalidArgs, int_valid[0]))
2018-06-22 22:59:46 +02:00
{
auto scenarioSetSetting = ScenarioSetSettingAction(
ScenarioSetSetting::GuestInitialHunger, (std::clamp(int_val[0], 1, 84) * 255 / 100 - 255) * -1);
scenarioSetSetting.SetCallback([&console](const GameAction*, const GameActions::Result* res) {
if (res->Error != GameActions::Status::Ok)
console.WriteLineError("set guest_initial_hunger command failed, likely due to permissions.");
else
console.Execute("get guest_initial_happiness");
});
GameActions::Execute(&scenarioSetSetting);
}
2023-01-16 21:14:50 +01:00
else if (argv[0] == "guest_initial_thirst" && InvalidArguments(&invalidArgs, int_valid[0]))
2018-06-22 22:59:46 +02:00
{
auto scenarioSetSetting = ScenarioSetSettingAction(
ScenarioSetSetting::GuestInitialThirst, (std::clamp(int_val[0], 1, 84) * 255 / 100 - 255) * -1);
scenarioSetSetting.SetCallback([&console](const GameAction*, const GameActions::Result* res) {
if (res->Error != GameActions::Status::Ok)
console.WriteLineError("set guest_initial_thirst command failed, likely due to permissions.");
else
console.Execute("get guest_initial_thirst");
});
GameActions::Execute(&scenarioSetSetting);
}
2023-01-16 21:14:50 +01:00
else if (argv[0] == "guest_prefer_less_intense_rides" && InvalidArguments(&invalidArgs, int_valid[0]))
2018-06-22 22:59:46 +02:00
{
auto scenarioSetSetting = ScenarioSetSettingAction(ScenarioSetSetting::GuestsPreferLessIntenseRides, int_val[0]);
scenarioSetSetting.SetCallback([&console](const GameAction*, const GameActions::Result* res) {
if (res->Error != GameActions::Status::Ok)
console.WriteLineError("set guest_prefer_less_intense_rides command failed, likely due to permissions.");
else
console.Execute("get guest_prefer_less_intense_rides");
});
GameActions::Execute(&scenarioSetSetting);
}
2023-01-16 21:14:50 +01:00
else if (argv[0] == "guest_prefer_more_intense_rides" && InvalidArguments(&invalidArgs, int_valid[0]))
2018-06-22 22:59:46 +02:00
{
auto scenarioSetSetting = ScenarioSetSettingAction(ScenarioSetSetting::GuestsPreferMoreIntenseRides, int_val[0]);
scenarioSetSetting.SetCallback([&console](const GameAction*, const GameActions::Result* res) {
if (res->Error != GameActions::Status::Ok)
console.WriteLineError("set guest_prefer_more_intense_rides command failed, likely due to permissions.");
else
console.Execute("get guest_prefer_more_intense_rides");
});
GameActions::Execute(&scenarioSetSetting);
}
2023-01-16 21:14:50 +01:00
else if (argv[0] == "forbid_marketing_campaigns" && InvalidArguments(&invalidArgs, int_valid[0]))
2018-06-22 22:59:46 +02:00
{
auto scenarioSetSetting = ScenarioSetSettingAction(ScenarioSetSetting::ForbidMarketingCampaigns, int_val[0]);
scenarioSetSetting.SetCallback([&console](const GameAction*, const GameActions::Result* res) {
if (res->Error != GameActions::Status::Ok)
console.WriteLineError("set forbid_marketing_campaigns command failed, likely due to permissions.");
else
console.Execute("get forbid_marketing_campaigns");
});
GameActions::Execute(&scenarioSetSetting);
}
2023-01-16 21:14:50 +01:00
else if (argv[0] == "forbid_landscape_changes" && InvalidArguments(&invalidArgs, int_valid[0]))
2018-06-22 22:59:46 +02:00
{
auto scenarioSetSetting = ScenarioSetSettingAction(ScenarioSetSetting::ForbidLandscapeChanges, int_val[0]);
scenarioSetSetting.SetCallback([&console](const GameAction*, const GameActions::Result* res) {
if (res->Error != GameActions::Status::Ok)
console.WriteLineError("set forbid_landscape_changes command failed, likely due to permissions.");
else
console.Execute("get forbid_landscape_changes");
});
GameActions::Execute(&scenarioSetSetting);
}
2023-01-16 21:14:50 +01:00
else if (argv[0] == "forbid_tree_removal" && InvalidArguments(&invalidArgs, int_valid[0]))
2018-06-22 22:59:46 +02:00
{
auto scenarioSetSetting = ScenarioSetSettingAction(ScenarioSetSetting::ForbidTreeRemoval, int_val[0]);
scenarioSetSetting.SetCallback([&console](const GameAction*, const GameActions::Result* res) {
if (res->Error != GameActions::Status::Ok)
console.WriteLineError("set forbid_tree_removal command failed, likely due to permissions.");
else
console.Execute("get forbid_tree_removal");
});
GameActions::Execute(&scenarioSetSetting);
}
2023-01-16 21:14:50 +01:00
else if (argv[0] == "forbid_high_construction" && InvalidArguments(&invalidArgs, int_valid[0]))
2018-06-22 22:59:46 +02:00
{
auto scenarioSetSetting = ScenarioSetSettingAction(ScenarioSetSetting::ForbidHighConstruction, int_val[0]);
scenarioSetSetting.SetCallback([&console](const GameAction*, const GameActions::Result* res) {
if (res->Error != GameActions::Status::Ok)
console.WriteLineError("set forbid_high_construction command failed, likely due to permissions.");
else
console.Execute("get forbid_high_construction");
});
GameActions::Execute(&scenarioSetSetting);
}
2023-01-16 21:14:50 +01:00
else if (argv[0] == "pay_for_rides" && InvalidArguments(&invalidArgs, int_valid[0]))
2018-06-22 22:59:46 +02:00
{
SET_FLAG(gameState.Park.Flags, PARK_FLAGS_PARK_FREE_ENTRY, int_val[0]);
2018-03-11 20:46:03 +01:00
console.Execute("get pay_for_rides");
}
2023-01-16 21:14:50 +01:00
else if (argv[0] == "no_money" && InvalidArguments(&invalidArgs, int_valid[0]))
2018-06-22 22:59:46 +02:00
{
auto cheatSetAction = CheatSetAction(CheatType::NoMoney, int_val[0] != 0);
cheatSetAction.SetCallback([&console](const GameAction*, const GameActions::Result* res) {
if (res->Error != GameActions::Status::Ok)
console.WriteLineError("set no_money command failed, likely due to permissions.");
else
console.Execute("get no_money");
});
GameActions::Execute(&cheatSetAction);
}
2023-01-16 21:14:50 +01:00
else if (argv[0] == "difficult_park_rating" && InvalidArguments(&invalidArgs, int_valid[0]))
2018-06-22 22:59:46 +02:00
{
auto scenarioSetSetting = ScenarioSetSettingAction(ScenarioSetSetting::ParkRatingHigherDifficultyLevel, int_val[0]);
scenarioSetSetting.SetCallback([&console](const GameAction*, const GameActions::Result* res) {
if (res->Error != GameActions::Status::Ok)
console.WriteLineError("set difficult_park_rating command failed, likely due to permissions.");
else
console.Execute("get difficult_park_rating");
});
GameActions::Execute(&scenarioSetSetting);
}
2023-01-16 21:14:50 +01:00
else if (argv[0] == "difficult_guest_generation" && InvalidArguments(&invalidArgs, int_valid[0]))
2018-06-22 22:59:46 +02:00
{
auto scenarioSetSetting = ScenarioSetSettingAction(
ScenarioSetSetting::GuestGenerationHigherDifficultyLevel, int_val[0]);
scenarioSetSetting.SetCallback([&console](const GameAction*, const GameActions::Result* res) {
if (res->Error != GameActions::Status::Ok)
console.WriteLineError("set difficult_guest_generation command failed, likely due to permissions.");
else
console.Execute("get difficult_guest_generation");
});
GameActions::Execute(&scenarioSetSetting);
}
2023-01-16 21:14:50 +01:00
else if (argv[0] == "park_open" && InvalidArguments(&invalidArgs, int_valid[0]))
2018-06-22 22:59:46 +02:00
{
auto parkSetParameter = ParkSetParameterAction((int_val[0] == 1) ? ParkParameter::Open : ParkParameter::Close);
parkSetParameter.SetCallback([&console](const GameAction*, const GameActions::Result* res) {
if (res->Error != GameActions::Status::Ok)
console.WriteLineError("set park_open command failed, likely due to permissions.");
else
console.Execute("get park_open");
});
GameActions::Execute(&parkSetParameter);
}
2023-01-16 21:14:50 +01:00
else if (argv[0] == "land_rights_cost" && InvalidArguments(&invalidArgs, double_valid[0]))
2018-06-22 22:59:46 +02:00
{
auto scenarioSetSetting = ScenarioSetSettingAction(
ScenarioSetSetting::CostToBuyLand, std::clamp(ToMoney64FromGBP(double_val[0]), 0.00_GBP, 200.00_GBP));
scenarioSetSetting.SetCallback([&console](const GameAction*, const GameActions::Result* res) {
if (res->Error != GameActions::Status::Ok)
console.WriteLineError("set land_rights_cost command failed, likely due to permissions.");
else
console.Execute("get land_rights_cost");
});
GameActions::Execute(&scenarioSetSetting);
}
2023-01-16 21:14:50 +01:00
else if (argv[0] == "construction_rights_cost" && InvalidArguments(&invalidArgs, double_valid[0]))
2018-06-22 22:59:46 +02:00
{
auto scenarioSetSetting = ScenarioSetSettingAction(
ScenarioSetSetting::CostToBuyConstructionRights,
std::clamp(ToMoney64FromGBP(double_val[0]), 0.00_GBP, 200.00_GBP));
scenarioSetSetting.SetCallback([&console](const GameAction*, const GameActions::Result* res) {
if (res->Error != GameActions::Status::Ok)
console.WriteLineError("set construction_rights_cost command failed, likely due to permissions.");
else
console.Execute("get construction_rights_cost");
});
GameActions::Execute(&scenarioSetSetting);
}
else if (argv[0] == "climate")
2018-06-22 22:59:46 +02:00
{
uint8_t newClimate = static_cast<uint8_t>(ClimateType::Count);
invalidArgs = true;
2018-06-22 22:59:46 +02:00
if (int_valid[0])
{
newClimate = static_cast<uint8_t>(int_val[0]);
invalidArgs = false;
}
2018-06-22 22:59:46 +02:00
else
{
for (newClimate = 0; newClimate < static_cast<uint8_t>(ClimateType::Count); newClimate++)
2018-06-22 22:59:46 +02:00
{
if (argv[1] == ClimateNames[newClimate])
2018-06-22 22:59:46 +02:00
{
invalidArgs = false;
break;
}
}
}
if (invalidArgs)
{
console.WriteLine(LanguageGetString(STR_INVALID_CLIMATE_ID));
}
else
{
auto gameAction = ClimateSetAction(ClimateType{ newClimate });
GameActions::Execute(&gameAction);
2018-06-22 22:59:46 +02:00
console.Execute("get climate");
}
}
2023-01-16 21:14:50 +01:00
else if (argv[0] == "game_speed" && InvalidArguments(&invalidArgs, int_valid[0]))
2018-06-22 22:59:46 +02:00
{
gGameSpeed = std::clamp(int_val[0], 1, 8);
2018-03-11 20:46:03 +01:00
console.Execute("get game_speed");
}
2023-01-16 21:14:50 +01:00
else if (argv[0] == "console_small_font" && InvalidArguments(&invalidArgs, int_valid[0]))
2018-06-22 22:59:46 +02:00
{
gConfigInterface.ConsoleSmallFont = (int_val[0] != 0);
ConfigSaveDefault();
2018-03-11 20:46:03 +01:00
console.Execute("get console_small_font");
}
2023-01-16 21:14:50 +01:00
else if (argv[0] == "location" && InvalidArguments(&invalidArgs, int_valid[0] && int_valid[1]))
2018-06-22 22:59:46 +02:00
{
WindowBase* w = WindowGetMain();
2018-06-22 22:59:46 +02:00
if (w != nullptr)
{
2020-03-01 23:18:59 +01:00
auto location = TileCoordsXYZ(int_val[0], int_val[1], 0).ToCoordsXYZ().ToTileCentre();
location.z = TileElementHeight(location);
w->SetLocation(location);
2023-01-16 21:14:50 +01:00
ViewportUpdatePosition(w);
2018-03-11 20:46:03 +01:00
console.Execute("get location");
}
}
2023-01-16 21:14:50 +01:00
else if (argv[0] == "window_scale" && InvalidArguments(&invalidArgs, double_valid[0]))
2018-06-22 22:59:46 +02:00
{
float newScale = static_cast<float>(0.001 * std::trunc(1000 * double_val[0]));
gConfigGeneral.WindowScale = std::clamp(newScale, 0.5f, 5.0f);
ConfigSaveDefault();
GfxInvalidateScreen();
2022-11-06 21:49:07 +01:00
ContextTriggerResize();
ContextUpdateCursorScale();
2018-03-11 20:46:03 +01:00
console.Execute("get window_scale");
}
2023-01-16 21:14:50 +01:00
else if (argv[0] == "window_limit" && InvalidArguments(&invalidArgs, int_valid[0]))
2018-06-22 22:59:46 +02:00
{
WindowSetWindowLimit(int_val[0]);
2018-03-11 20:46:03 +01:00
console.Execute("get window_limit");
}
2023-01-16 21:14:50 +01:00
else if (argv[0] == "render_weather_effects" && InvalidArguments(&invalidArgs, int_valid[0]))
2018-06-22 22:59:46 +02:00
{
gConfigGeneral.RenderWeatherEffects = (int_val[0] != 0);
ConfigSaveDefault();
2018-03-11 20:46:03 +01:00
console.Execute("get render_weather_effects");
}
2023-01-16 21:14:50 +01:00
else if (argv[0] == "render_weather_gloom" && InvalidArguments(&invalidArgs, int_valid[0]))
2018-06-22 22:59:46 +02:00
{
gConfigGeneral.RenderWeatherGloom = (int_val[0] != 0);
ConfigSaveDefault();
2018-03-11 20:46:03 +01:00
console.Execute("get render_weather_gloom");
}
2023-01-16 21:14:50 +01:00
else if (argv[0] == "cheat_sandbox_mode" && InvalidArguments(&invalidArgs, int_valid[0]))
2018-06-22 22:59:46 +02:00
{
2024-03-03 22:44:15 +01:00
if (GetGameState().Cheats.SandboxMode != (int_val[0] != 0))
2018-06-22 22:59:46 +02:00
{
auto cheatSetAction = CheatSetAction(CheatType::SandboxMode, int_val[0] != 0);
cheatSetAction.SetCallback([&console](const GameAction*, const GameActions::Result* res) {
if (res->Error != GameActions::Status::Ok)
console.WriteLineError("Network error: Permission denied!");
else
console.Execute("get cheat_sandbox_mode");
});
GameActions::Execute(&cheatSetAction);
}
else
{
console.Execute("get cheat_sandbox_mode");
}
}
2023-01-16 21:14:50 +01:00
else if (argv[0] == "cheat_disable_clearance_checks" && InvalidArguments(&invalidArgs, int_valid[0]))
2018-06-22 22:59:46 +02:00
{
2024-03-03 22:44:15 +01:00
if (GetGameState().Cheats.DisableClearanceChecks != (int_val[0] != 0))
2018-06-22 22:59:46 +02:00
{
auto cheatSetAction = CheatSetAction(CheatType::DisableClearanceChecks, int_val[0] != 0);
cheatSetAction.SetCallback([&console](const GameAction*, const GameActions::Result* res) {
if (res->Error != GameActions::Status::Ok)
console.WriteLineError("Network error: Permission denied!");
else
console.Execute("get cheat_disable_clearance_checks");
});
GameActions::Execute(&cheatSetAction);
}
else
{
console.Execute("get cheat_disable_clearance_checks");
}
}
2023-01-16 21:14:50 +01:00
else if (argv[0] == "cheat_disable_support_limits" && InvalidArguments(&invalidArgs, int_valid[0]))
2018-06-22 22:59:46 +02:00
{
2024-03-03 22:44:15 +01:00
if (GetGameState().Cheats.DisableSupportLimits != (int_val[0] != 0))
2018-06-22 22:59:46 +02:00
{
auto cheatSetAction = CheatSetAction(CheatType::DisableSupportLimits, int_val[0] != 0);
cheatSetAction.SetCallback([&console](const GameAction*, const GameActions::Result* res) {
if (res->Error != GameActions::Status::Ok)
console.WriteLineError("Network error: Permission denied!");
else
console.Execute("get cheat_disable_support_limits");
});
GameActions::Execute(&cheatSetAction);
}
else
{
console.Execute("get cheat_disable_support_limits");
}
}
2023-01-16 21:14:50 +01:00
else if (argv[0] == "current_rotation" && InvalidArguments(&invalidArgs, int_valid[0]))
{
2023-01-16 21:14:50 +01:00
uint8_t currentRotation = GetCurrentRotation();
int32_t newRotation = int_val[0];
if (newRotation < 0 || newRotation > 3)
{
console.WriteLineError("Invalid argument. Valid rotations are 0-3.");
}
else if (newRotation != currentRotation)
{
ViewportRotateAll(newRotation - currentRotation);
}
console.Execute("get current_rotation");
}
2023-01-16 21:14:50 +01:00
else if (argv[0] == "host_timescale" && InvalidArguments(&invalidArgs, double_valid[0]))
{
float newScale = static_cast<float>(double_val[0]);
OpenRCT2::GetContext()->SetTimeScale(newScale);
console.Execute("get host_timescale");
}
#ifndef NO_TTF
2023-01-16 21:14:50 +01:00
else if (argv[0] == "enable_hinting" && InvalidArguments(&invalidArgs, int_valid[0]))
2018-06-22 22:59:46 +02:00
{
2022-10-16 17:55:49 +02:00
gConfigFonts.EnableHinting = (int_val[0] != 0);
ConfigSaveDefault();
2018-03-11 20:46:03 +01:00
console.Execute("get enable_hinting");
TTFToggleHinting();
}
#endif
2018-06-22 22:59:46 +02:00
else if (invalidArgs)
{
2018-03-11 20:46:03 +01:00
console.WriteLineError("Invalid arguments.");
}
2018-06-22 22:59:46 +02:00
else
{
2018-03-11 20:46:03 +01:00
console.WriteLineError("Invalid variable.");
}
GfxInvalidateScreen();
}
2018-06-22 22:59:46 +02:00
else
{
2018-03-11 20:46:03 +01:00
console.WriteLineError("Value required.");
}
return 0;
2015-05-19 04:53:37 +02:00
}
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandLoadObject(InteractiveConsole& console, const arguments_t& argv)
2018-06-22 22:59:46 +02:00
{
if (!argv.empty())
2018-06-22 22:59:46 +02:00
{
char name[9] = { 0 };
std::fill_n(name, 8, ' ');
std::size_t i = 0;
for (const char* ch = argv[0].c_str(); *ch != '\0' && i < std::size(name) - 1; ch++)
2018-06-22 22:59:46 +02:00
{
name[i++] = *ch;
}
const ObjectRepositoryItem* ori = ObjectRepositoryFindObjectByName(name);
2018-06-22 22:59:46 +02:00
if (ori == nullptr)
{
2018-03-11 20:46:03 +01:00
console.WriteLineError("Could not find the object.");
return 1;
}
const RCTObjectEntry* entry = &ori->ObjectEntry;
const auto* loadedObject = ObjectManagerGetLoadedObject(ObjectEntryDescriptor(*ori));
2018-06-22 22:59:46 +02:00
if (loadedObject != nullptr)
{
2018-03-11 20:46:03 +01:00
console.WriteLineError("Object is already in scenario.");
return 1;
}
loadedObject = ObjectManagerLoadObject(entry);
2018-06-22 22:59:46 +02:00
if (loadedObject == nullptr)
{
2018-03-11 20:46:03 +01:00
console.WriteLineError("Unable to load object.");
return 1;
}
auto groupIndex = ObjectManagerGetLoadedObjectEntryIndex(loadedObject);
ObjectType objectType = entry->GetType();
if (objectType == ObjectType::Ride)
2018-06-22 22:59:46 +02:00
{
// Automatically research the ride so it's supported by the game.
const auto* rideEntry = GetRideEntryByIndex(groupIndex);
for (int32_t j = 0; j < RCT2::ObjectLimits::MaxRideTypesPerRideEntry; j++)
2018-06-22 22:59:46 +02:00
{
auto rideType = rideEntry->ride_type[j];
2020-04-30 13:58:48 +02:00
if (rideType != RIDE_TYPE_NULL)
{
ResearchCategory category = GetRideTypeDescriptor(rideType).GetResearchCategory();
ResearchInsertRideEntry(rideType, groupIndex, category, true);
2020-04-30 13:58:48 +02:00
}
}
gSilentResearch = true;
ResearchResetCurrentItem();
gSilentResearch = false;
}
else if (objectType == ObjectType::SceneryGroup)
2018-06-22 22:59:46 +02:00
{
ResearchInsertSceneryGroupEntry(groupIndex, true);
gSilentResearch = true;
ResearchResetCurrentItem();
gSilentResearch = false;
}
ScenerySetDefaultPlacementConfiguration();
2017-10-07 01:28:00 +02:00
2018-02-05 22:59:44 +01:00
auto intent = Intent(INTENT_ACTION_REFRESH_NEW_RIDES);
2022-11-06 21:49:07 +01:00
ContextBroadcastIntent(&intent);
gWindowUpdateTicks = 0;
GfxInvalidateScreen();
2018-03-11 20:46:03 +01:00
console.WriteLine("Object file loaded.");
}
return 0;
}
constexpr std::array _objectTypeNames = {
"Rides",
"Small Scenery",
"Large Scenery",
"Walls",
"Banners",
"Paths",
"Path Additions",
"Scenery groups",
"Park entrances",
"Water",
"ScenarioText",
"Terrain Surface",
"Terrain Edges",
"Stations",
"Music",
"Footpath Surface",
"Footpath Railings",
"Audio",
};
static_assert(_objectTypeNames.size() == EnumValue(ObjectType::Count));
2022-08-31 22:35:14 +02:00
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandCountObjects(InteractiveConsole& console, [[maybe_unused]] const arguments_t& argv)
{
for (auto objectType : ObjectTypes)
2018-06-22 22:59:46 +02:00
{
int32_t entryGroupIndex = 0;
for (; entryGroupIndex < object_entry_group_counts[EnumValue(objectType)]; entryGroupIndex++)
2018-06-22 22:59:46 +02:00
{
if (ObjectEntryGetObject(objectType, entryGroupIndex) == nullptr)
{
break;
}
}
console.WriteFormatLine(
"%s: %d/%d", _objectTypeNames[EnumValue(objectType)], entryGroupIndex,
object_entry_group_counts[EnumValue(objectType)]);
}
return 0;
}
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandOpen(InteractiveConsole& console, const arguments_t& argv)
2018-06-22 22:59:46 +02:00
{
if (!argv.empty())
2018-06-22 22:59:46 +02:00
{
bool title = (gScreenFlags & SCREEN_FLAGS_TITLE_DEMO) != 0;
bool invalidTitle = false;
2023-01-16 21:14:50 +01:00
if (argv[0] == "object_selection" && InvalidArguments(&invalidTitle, !title))
2018-06-22 22:59:46 +02:00
{
if (NetworkGetMode() != NETWORK_MODE_NONE)
{
console.WriteLineError("Cannot open this window in multiplayer mode.");
}
else
{
// Only this window should be open for safety reasons
WindowCloseAll();
2022-11-06 21:49:07 +01:00
ContextOpenWindow(WindowClass::EditorObjectSelection);
}
2018-06-22 22:59:46 +02:00
}
2023-01-16 21:14:50 +01:00
else if (argv[0] == "inventions_list" && InvalidArguments(&invalidTitle, !title))
2018-06-22 22:59:46 +02:00
{
if (NetworkGetMode() != NETWORK_MODE_NONE)
{
console.WriteLineError("Cannot open this window in multiplayer mode.");
}
else
{
2022-11-06 21:49:07 +01:00
ContextOpenWindow(WindowClass::EditorInventionList);
}
2018-06-22 22:59:46 +02:00
}
2023-01-16 21:14:50 +01:00
else if (argv[0] == "scenario_options" && InvalidArguments(&invalidTitle, !title))
2018-06-22 22:59:46 +02:00
{
2022-11-06 21:49:07 +01:00
ContextOpenWindow(WindowClass::EditorScenarioOptions);
2018-06-22 22:59:46 +02:00
}
2023-01-16 21:14:50 +01:00
else if (argv[0] == "objective_options" && InvalidArguments(&invalidTitle, !title))
{
if (NetworkGetMode() != NETWORK_MODE_NONE)
{
console.WriteLineError("Cannot open this window in multiplayer mode.");
}
else
{
2022-11-06 21:49:07 +01:00
ContextOpenWindow(WindowClass::EditorObjectiveOptions);
}
}
else if (argv[0] == "options")
2018-06-22 22:59:46 +02:00
{
2022-11-06 21:49:07 +01:00
ContextOpenWindow(WindowClass::Options);
2018-06-22 22:59:46 +02:00
}
else if (argv[0] == "themes")
2018-06-22 22:59:46 +02:00
{
2022-11-06 21:49:07 +01:00
ContextOpenWindow(WindowClass::Themes);
2018-06-22 22:59:46 +02:00
}
else if (invalidTitle)
{
2018-03-11 20:46:03 +01:00
console.WriteLineError("Cannot open this window in the title screen.");
2018-06-22 22:59:46 +02:00
}
else
{
2018-03-11 20:46:03 +01:00
console.WriteLineError("Invalid window.");
}
}
return 0;
}
2015-05-19 04:53:37 +02:00
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandRemoveUnusedObjects(InteractiveConsole& console, [[maybe_unused]] const arguments_t& argv)
{
int32_t result = EditorRemoveUnusedObjects();
2018-03-11 20:46:03 +01:00
console.WriteFormatLine("%d unused object entries have been removed.", result);
return 0;
}
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandRemoveFloatingObjects(InteractiveConsole& console, const arguments_t& argv)
{
2021-11-24 14:37:47 +01:00
uint16_t result = RemoveFloatingEntities();
console.WriteFormatLine("Removed %d flying objects", result);
return 0;
}
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandRemoveParkFences(InteractiveConsole& console, [[maybe_unused]] const arguments_t& argv)
2017-10-08 10:12:00 +02:00
{
auto action = CheatSetAction(CheatType::RemoveParkFences);
GameActions::Execute(&action);
2018-03-11 20:46:03 +01:00
console.WriteFormatLine("Park fences have been removed.");
2017-10-08 10:12:00 +02:00
return 0;
}
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandShowLimits(InteractiveConsole& console, [[maybe_unused]] const arguments_t& argv)
{
const auto& tileElements = GetTileElements();
2021-05-30 10:09:47 +02:00
const auto tileElementCount = tileElements.size();
int32_t rideCount = RideGetCount();
int32_t spriteCount = 0;
for (int32_t i = 0; i < static_cast<uint8_t>(EntityType::Count); ++i)
{
spriteCount += GetEntityListCount(EntityType(i));
}
auto bannerCount = GetNumBanners();
console.WriteFormatLine("Sprites: %d/%d", spriteCount, MAX_ENTITIES);
2021-05-30 10:09:47 +02:00
console.WriteFormatLine("Map Elements: %zu/%d", tileElementCount, MAX_TILE_ELEMENTS);
console.WriteFormatLine("Banners: %d/%zu", bannerCount, MAX_BANNERS);
console.WriteFormatLine("Rides: %d/%d", rideCount, OpenRCT2::Limits::MaxRidesInPark);
console.WriteFormatLine("Images: %zu/%zu", ImageListGetUsedCount(), ImageListGetMaximum());
return 0;
}
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandForceDate([[maybe_unused]] InteractiveConsole& console, [[maybe_unused]] const arguments_t& argv)
{
int32_t year = 0;
int32_t month = 0;
int32_t day = 0;
if (argv.size() < 1 || argv.size() > 3)
{
return -1;
}
// All cases involve providing a year, so grab that first
year = atoi(argv[0].c_str());
if (year < 1 || year > kMaxYear)
{
return -1;
}
// YYYY (no month provided, preserve existing month)
if (argv.size() == 1)
{
month = GetDate().GetMonth() + 1;
}
// YYYY MM or YYYY MM DD (month provided)
if (argv.size() >= 2)
{
month = atoi(argv[1].c_str());
month -= 2;
if (month < 1 || month > MONTH_COUNT)
{
return -1;
}
}
// YYYY OR YYYY MM (no day provided, preserve existing day)
if (argv.size() <= 2)
{
day = std::clamp(GetDate().GetDay() + 1, 1, static_cast<int>(Date::GetDaysInMonth(month - 1)));
}
// YYYY MM DD (year, month, and day provided)
if (argv.size() == 3)
{
day = atoi(argv[2].c_str());
if (day < 1 || day > Date::GetDaysInMonth(month - 1))
{
return -1;
}
}
auto setDateAction = ParkSetDateAction(year - 1, month - 1, day - 1);
GameActions::Execute(&setDateAction);
WindowInvalidateByClass(WindowClass::BottomToolbar);
return 1;
}
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandLoadPark([[maybe_unused]] InteractiveConsole& console, [[maybe_unused]] const arguments_t& argv)
{
if (argv.size() < 1)
{
console.WriteLine("Parameters required <filename>");
return 0;
}
u8string savePath = {};
if (String::IndexOf(argv[0].c_str(), '/') == SIZE_MAX && String::IndexOf(argv[0].c_str(), '\\') == SIZE_MAX)
{
// no / or \ was included. File should be in save dir.
auto env = OpenRCT2::GetContext()->GetPlatformEnvironment();
auto directory = env->GetDirectoryPath(OpenRCT2::DIRBASE::USER, OpenRCT2::DIRID::SAVE);
savePath = Path::Combine(directory, argv[0]);
}
else
{
savePath = argv[0];
}
if (!String::EndsWith(savePath, ".sv6", true) && !String::EndsWith(savePath, ".sc6", true)
&& !String::EndsWith(savePath, ".park", true))
{
savePath += ".park";
}
if (OpenRCT2::GetContext()->LoadParkFromFile(savePath))
{
console.WriteFormatLine("Park %s was loaded successfully", savePath.c_str());
}
else
{
console.WriteFormatLine("Loading Park %s failed", savePath.c_str());
}
return 1;
}
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandSavePark([[maybe_unused]] InteractiveConsole& console, [[maybe_unused]] const arguments_t& argv)
{
if (argv.size() < 1)
{
SaveGameCmd();
}
else
{
SaveGameCmd(argv[0].c_str());
}
return 1;
}
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandSay(InteractiveConsole& console, const arguments_t& argv)
{
if (NetworkGetMode() == NETWORK_MODE_NONE || NetworkGetStatus() != NETWORK_STATUS_CONNECTED
|| NetworkGetAuthstatus() != NetworkAuth::Ok)
{
console.WriteFormatLine("This command only works in multiplayer mode.");
return 0;
}
2021-09-15 22:22:15 +02:00
if (!argv.empty())
{
NetworkSendChat(argv[0].c_str());
2021-09-15 22:22:15 +02:00
return 1;
}
2021-09-15 22:22:15 +02:00
console.WriteFormatLine("Input your message");
return 0;
}
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandReplayStartRecord(InteractiveConsole& console, const arguments_t& argv)
{
if (NetworkGetMode() != NETWORK_MODE_NONE)
{
console.WriteFormatLine("This command is currently not supported in multiplayer mode.");
return 0;
}
if (argv.size() < 1)
{
console.WriteFormatLine("Parameters required <replay_name> [<max_ticks = 0xFFFFFFFF>]");
return 0;
}
std::string name = argv[0];
2018-12-28 20:31:07 +01:00
if (!String::EndsWith(name, ".parkrep", true))
2020-03-31 23:12:35 +02:00
{
name += ".parkrep";
2020-03-31 23:12:35 +02:00
}
std::string outPath = OpenRCT2::GetContext()->GetPlatformEnvironment()->GetDirectoryPath(
OpenRCT2::DIRBASE::USER, OpenRCT2::DIRID::REPLAY);
name = Path::Combine(outPath, name);
2018-12-28 20:31:07 +01:00
// If ticks are specified by user use that otherwise maximum ticks specified by const.
uint32_t maxTicks = OpenRCT2::k_MaxReplayTicks;
if (argv.size() >= 2)
{
maxTicks = atol(argv[1].c_str());
}
auto* replayManager = OpenRCT2::GetContext()->GetReplayManager();
2018-12-28 20:31:07 +01:00
if (replayManager->StartRecording(name, maxTicks))
{
OpenRCT2::ReplayRecordInfo info;
replayManager->GetCurrentReplayInfo(info);
const char* logFmt = "Replay recording started: (%s) %s";
console.WriteFormatLine(logFmt, info.Name.c_str(), info.FilePath.c_str());
Console::WriteLine(logFmt, info.Name.c_str(), info.FilePath.c_str());
2018-12-28 20:31:07 +01:00
return 1;
}
return 0;
}
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandReplayStopRecord(InteractiveConsole& console, const arguments_t& argv)
{
if (NetworkGetMode() != NETWORK_MODE_NONE)
{
console.WriteFormatLine("This command is currently not supported in multiplayer mode.");
return 0;
}
auto* replayManager = OpenRCT2::GetContext()->GetReplayManager();
2019-05-10 22:00:38 +02:00
if (!replayManager->IsRecording() && !replayManager->IsNormalising())
{
console.WriteFormatLine("Replay currently not recording");
return 0;
}
OpenRCT2::ReplayRecordInfo info;
replayManager->GetCurrentReplayInfo(info);
2018-12-28 20:31:07 +01:00
if (replayManager->StopRecording())
{
const char* logFmt = "Replay recording stopped: (%s) %s\n"
" Ticks: %u\n"
" Commands: %u\n"
" Checksums: %u";
console.WriteFormatLine(
logFmt, info.Name.c_str(), info.FilePath.c_str(), info.Ticks, info.NumCommands, info.NumChecksums);
Console::WriteLine(logFmt, info.Name.c_str(), info.FilePath.c_str(), info.Ticks, info.NumCommands, info.NumChecksums);
2018-12-28 20:31:07 +01:00
return 1;
}
return 0;
}
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandReplayStart(InteractiveConsole& console, const arguments_t& argv)
{
if (NetworkGetMode() != NETWORK_MODE_NONE)
{
console.WriteFormatLine("This command is currently not supported in multiplayer mode.");
return 0;
}
if (argv.size() < 1)
{
console.WriteFormatLine("Parameters required <replay_name>");
return 0;
}
std::string name = argv[0];
auto* replayManager = OpenRCT2::GetContext()->GetReplayManager();
2018-12-28 20:31:07 +01:00
if (replayManager->StartPlayback(name))
{
OpenRCT2::ReplayRecordInfo info;
replayManager->GetCurrentReplayInfo(info);
std::time_t ts = info.TimeRecorded;
char recordingDate[128] = {};
std::strftime(recordingDate, sizeof(recordingDate), "%c", std::localtime(&ts));
const char* logFmt = "Replay playback started: %s\n"
" Date Recorded: %s\n"
" Ticks: %u\n"
" Commands: %u\n"
" Checksums: %u";
console.WriteFormatLine(logFmt, info.FilePath.c_str(), recordingDate, info.Ticks, info.NumCommands, info.NumChecksums);
Console::WriteLine(logFmt, info.FilePath.c_str(), recordingDate, info.Ticks, info.NumCommands, info.NumChecksums);
2018-12-28 20:31:07 +01:00
return 1;
}
return 0;
}
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandReplayStop(InteractiveConsole& console, const arguments_t& argv)
{
if (NetworkGetMode() != NETWORK_MODE_NONE)
{
console.WriteFormatLine("This command is currently not supported in multiplayer mode.");
return 0;
}
auto* replayManager = OpenRCT2::GetContext()->GetReplayManager();
2018-12-28 20:31:07 +01:00
if (replayManager->StopPlayback())
{
2018-12-28 20:31:07 +01:00
console.WriteFormatLine("Stopped replay");
return 1;
}
return 0;
}
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandReplayNormalise(InteractiveConsole& console, const arguments_t& argv)
{
if (NetworkGetMode() != NETWORK_MODE_NONE)
{
console.WriteFormatLine("This command is currently not supported in multiplayer mode.");
return 0;
}
if (argv.size() < 2)
{
console.WriteFormatLine("Parameters required <replay_input> <replay_output>");
return 0;
}
std::string inputFile = argv[0];
std::string outputFile = argv[1];
if (!String::EndsWith(outputFile, ".parkrep", true))
{
outputFile += ".parkrep";
}
std::string outPath = OpenRCT2::GetContext()->GetPlatformEnvironment()->GetDirectoryPath(
OpenRCT2::DIRBASE::USER, OpenRCT2::DIRID::REPLAY);
outputFile = Path::Combine(outPath, outputFile);
auto* replayManager = OpenRCT2::GetContext()->GetReplayManager();
2018-12-28 20:31:07 +01:00
if (replayManager->NormaliseReplay(inputFile, outputFile))
{
2018-12-28 20:31:07 +01:00
console.WriteFormatLine("Stopped replay");
return 1;
}
return 0;
}
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandMpDesync(InteractiveConsole& console, const arguments_t& argv)
{
int32_t desyncType = 0;
if (argv.size() >= 1)
{
desyncType = atoi(argv[0].c_str());
}
std::vector<Guest*> guests;
for (auto* guest : EntityList<Guest>())
{
guests.push_back(guest);
}
switch (desyncType)
{
case 0: // Guest t-shirts.
{
if (guests.empty())
{
console.WriteFormatLine("No guests");
}
else
{
auto* guest = guests[0];
if (guests.size() > 1)
guest = guests[UtilRand() % guests.size() - 1];
guest->TshirtColour = UtilRand() & 0xFF;
guest->Invalidate();
}
break;
}
case 1: // Remove random guest.
{
if (guests.empty())
{
console.WriteFormatLine("No guest removed");
}
else
{
auto* guest = guests[0];
if (guests.size() > 1)
guest = guests[UtilRand() % guests.size() - 1];
guest->Remove();
}
break;
}
}
return 0;
}
#pragma warning(push)
#pragma warning(disable : 4702) // unreachable code
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandAbort([[maybe_unused]] InteractiveConsole& console, [[maybe_unused]] const arguments_t& argv)
{
std::abort();
return 0;
}
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandDereference([[maybe_unused]] InteractiveConsole& console, [[maybe_unused]] const arguments_t& argv)
{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnull-dereference"
// Dereference a nullptr to induce a crash to be caught by crash handler, on supported platforms
uint8_t* myptr = nullptr;
*myptr = 42;
return 0;
#pragma GCC diagnostic pop
}
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandTerminate([[maybe_unused]] InteractiveConsole& console, [[maybe_unused]] const arguments_t& argv)
{
std::terminate();
return 0;
}
#pragma warning(pop)
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandAssert([[maybe_unused]] InteractiveConsole& console, [[maybe_unused]] const arguments_t& argv)
{
if (!argv.empty())
Guard::Assert(false, "%s", argv[0].c_str());
else
Guard::Assert(false);
return 0;
}
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandAddNewsItem([[maybe_unused]] InteractiveConsole& console, [[maybe_unused]] const arguments_t& argv)
{
if (argv.size() < 2)
{
console.WriteLineWarning("Too few arguments");
static_assert(News::ItemTypeCount == 11, "News::ItemType::Count changed, update console command!");
console.WriteLine("add_news_item <type> <message> [assoc]");
console.WriteLine("type is one of:");
console.WriteLine(" 0 (News::ItemType::Null)");
console.WriteLine(" 1 (News::ItemType::Ride)");
console.WriteLine(" 2 (News::ItemType::PeepOnRide)");
console.WriteLine(" 3 (News::ItemType::Peep)");
console.WriteLine(" 4 (News::ItemType::Money)");
console.WriteLine(" 5 (News::ItemType::Blank)");
console.WriteLine(" 6 (News::ItemType::Research)");
console.WriteLine(" 7 (News::ItemType::Peeps)");
console.WriteLine(" 8 (News::ItemType::Award)");
console.WriteLine(" 9 (News::ItemType::Graph)");
console.WriteLine(" 10 (News::ItemType::Campaign)");
console.WriteLine("message is the message to display, wrapped in quotes for multiple words");
console.WriteLine("assoc is the associated id of ride/peep/tile/etc. If the selected ItemType doesn't need an assoc "
"(Null, Money, Award, Graph), you can leave this field blank");
return 1;
}
auto type = atoi(argv[0].c_str());
auto msg = argv[1].c_str();
auto assoc = 0;
News::ItemType itemType = static_cast<News::ItemType>(type);
if (argv.size() == 3) // 3 arguments passed, set assoc
{
assoc = atoi(argv[2].c_str());
}
else
{
if (News::CheckIfItemRequiresAssoc(itemType))
{
console.WriteLine("Selected ItemType requires an assoc");
return 0;
}
}
News::AddItemToQueue(itemType, msg, assoc);
console.WriteLine("Successfully added News Item");
return 0;
}
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandProfilerReset(
[[maybe_unused]] InteractiveConsole& console, [[maybe_unused]] const arguments_t& argv)
{
OpenRCT2::Profiling::ResetData();
return 0;
}
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandProfilerStart(
[[maybe_unused]] InteractiveConsole& console, [[maybe_unused]] const arguments_t& argv)
{
if (!OpenRCT2::Profiling::IsEnabled())
console.WriteLine("Started profiler");
OpenRCT2::Profiling::Enable();
return 0;
}
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandProfilerExportCSV(
[[maybe_unused]] InteractiveConsole& console, [[maybe_unused]] const arguments_t& argv)
{
if (argv.size() < 1)
{
console.WriteLineError("Missing argument: <file path>");
return 1;
}
const auto& csvFilePath = argv[0];
if (!OpenRCT2::Profiling::ExportCSV(csvFilePath))
{
console.WriteFormatLine("Unable to export CSV file to %s", csvFilePath.c_str());
return 1;
}
console.WriteFormatLine("Wrote file CSV file: \"%s\"", csvFilePath.c_str());
return 0;
}
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandProfilerStop(
[[maybe_unused]] InteractiveConsole& console, [[maybe_unused]] const arguments_t& argv)
{
if (OpenRCT2::Profiling::IsEnabled())
console.WriteLine("Stopped profiler");
OpenRCT2::Profiling::Disable();
// Export to CSV if argument is provided.
if (argv.size() >= 1)
{
2023-01-16 21:14:50 +01:00
return ConsoleCommandProfilerExportCSV(console, argv);
}
return 0;
}
static int32_t ConsoleSpawnBalloon(InteractiveConsole& console, const arguments_t& argv)
{
if (argv.size() < 3)
{
console.WriteLineError("Need arguments: <x> <y> <z> <colour>");
return 1;
}
int32_t x = COORDS_XY_STEP * atof(argv[0].c_str());
int32_t y = COORDS_XY_STEP * atof(argv[1].c_str());
int32_t z = COORDS_Z_STEP * atof(argv[2].c_str());
int32_t col = 28;
if (argv.size() > 3)
col = atoi(argv[3].c_str());
Balloon::Create({ x, y, z }, col, false);
return 0;
}
using console_command_func = int32_t (*)(InteractiveConsole& console, const arguments_t& argv);
struct ConsoleCommand
2018-06-22 22:59:46 +02:00
{
const utf8* command;
console_command_func func;
2018-06-22 22:59:46 +02:00
const utf8* help;
const utf8* usage;
};
2015-05-19 04:53:37 +02:00
// clang-format off
2018-01-11 23:14:31 +01:00
static constexpr const utf8* console_variable_table[] = {
"park_rating",
"park_value",
"company_value",
"money",
"scenario_initial_cash",
"current_loan",
"max_loan",
"guest_initial_cash",
"guest_initial_happiness",
"guest_initial_hunger",
"guest_initial_thirst",
"guest_prefer_less_intense_rides",
"guest_prefer_more_intense_rides",
"forbid_marketing_campaigns",
"forbid_landscape_changes",
"forbid_tree_removal",
"forbid_high_construction",
"pay_for_rides",
"no_money",
"difficult_park_rating",
"difficult_guest_generation",
"land_rights_cost",
"construction_rights_cost",
"park_open",
"climate",
"game_speed",
"console_small_font",
"location",
"window_scale",
"window_limit",
"render_weather_effects",
"render_weather_gloom",
"cheat_sandbox_mode",
"cheat_disable_clearance_checks",
"cheat_disable_support_limits",
"current_rotation",
};
2018-01-11 23:14:31 +01:00
static constexpr const utf8* console_window_table[] = {
"object_selection",
"inventions_list",
"scenario_options",
"objective_options",
"options",
"themes",
"title_sequences",
};
// clang-format on
static constexpr ConsoleCommand console_command_table[] = {
2023-01-16 21:14:50 +01:00
{ "abort", ConsoleCommandAbort, "Calls std::abort(), for testing purposes only.", "abort" },
{ "add_news_item", ConsoleCommandAddNewsItem, "Inserts a news item", "add_news_item [<type> <message> <assoc>]" },
{ "assert", ConsoleCommandAssert, "Triggers assertion failure, for testing purposes only", "assert" },
{ "clear", ConsoleCommandClear, "Clears the console.", "clear" },
{ "close", ConsoleCommandClose, "Closes the console.", "close" },
{ "date", ConsoleCommandForceDate, "Sets the date to a given date.", "Format <year>[ <month>[ <day>]]." },
{ "dereference", ConsoleCommandDereference, "Dereferences a nullptr, for testing purposes only", "dereference" },
{ "echo", ConsoleCommandEcho, "Echoes the text to the console.", "echo <text>" },
{ "exit", ConsoleCommandClose, "Closes the console.", "exit" },
{ "get", ConsoleCommandGet, "Gets the value of the specified variable.", "get <variable>" },
{ "help", ConsoleCommandHelp, "Lists commands or info about a command.", "help [command]" },
{ "hide", ConsoleCommandHide, "Hides the console.", "hide" },
{ "load_object", ConsoleCommandLoadObject,
"Loads the object file into the scenario.\n"
"Loading a scenery group will not load its associated objects.\n"
"This is a safer method opposed to \"open object_selection\".",
"load_object <objectfilenodat>" },
2023-01-16 21:14:50 +01:00
{ "load_park", ConsoleCommandLoadPark, "Load park from save directory or by absolute path", "load_park <filename>" },
{ "object_count", ConsoleCommandCountObjects, "Shows the number of objects of each type in the scenario.", "object_count" },
{ "open", ConsoleCommandOpen, "Opens the window with the give name.", "open <window>." },
{ "quit", ConsoleCommandClose, "Closes the console.", "quit" },
{ "remove_park_fences", ConsoleCommandRemoveParkFences, "Removes all park fences from the surface", "remove_park_fences" },
{ "remove_unused_objects", ConsoleCommandRemoveUnusedObjects, "Removes all the unused objects from the object selection.",
"remove_unused_objects" },
2023-01-16 21:14:50 +01:00
{ "remove_floating_objects", ConsoleCommandRemoveFloatingObjects, "Removes floating objects", "remove_floating_objects" },
{ "rides", ConsoleCommandRides, "Ride management.", "rides <subcommand>" },
{ "save_park", ConsoleCommandSavePark, "Save current state of park. If no name specified default path will be used.",
"save_park [name]" },
2023-01-16 21:14:50 +01:00
{ "say", ConsoleCommandSay, "Say to other players.", "say <message>" },
{ "set", ConsoleCommandSet, "Sets the variable to the specified value.", "set <variable> <value>" },
{ "show_limits", ConsoleCommandShowLimits, "Shows the map data counts and limits.", "show_limits" },
{ "spawn_balloon", ConsoleSpawnBalloon, "Spawns a balloon.", "spawn_balloon <x> <y> <z> <colour>" },
2023-01-16 21:14:50 +01:00
{ "staff", ConsoleCommandStaff, "Staff management.", "staff <subcommand>" },
{ "terminate", ConsoleCommandTerminate, "Calls std::terminate(), for testing purposes only.", "terminate" },
{ "variables", ConsoleCommandVariables, "Lists all the variables that can be used with get and sometimes set.",
"variables" },
{ "windows", ConsoleCommandWindows, "Lists all the windows that can be opened.", "windows" },
{ "replay_startrecord", ConsoleCommandReplayStartRecord, "Starts recording a new replay.",
"replay_startrecord <name> [max_ticks]" },
{ "replay_stoprecord", ConsoleCommandReplayStopRecord, "Stops recording a new replay.", "replay_stoprecord" },
{ "replay_start", ConsoleCommandReplayStart, "Starts a replay", "replay_start <name>" },
{ "replay_stop", ConsoleCommandReplayStop, "Stops the replay", "replay_stop" },
{ "replay_normalise", ConsoleCommandReplayNormalise, "Normalises the replay to remove all gaps",
"replay_normalise <input file> <output file>" },
2023-01-16 21:14:50 +01:00
{ "mp_desync", ConsoleCommandMpDesync, "Forces a multiplayer desync",
"ConsoleCommandMpDesync [desync_type, 0 = Random t-shirt color on random guest, 1 = Remove random guest ]" },
{ "profiler_reset", ConsoleCommandProfilerReset, "Resets the profiler data.", "profiler_reset" },
{ "profiler_start", ConsoleCommandProfilerStart, "Starts the profiler.", "profiler_start" },
{ "profiler_stop", ConsoleCommandProfilerStop, "Stops the profiler.", "profiler_stop [<output file>]" },
{ "profiler_exportcsv", ConsoleCommandProfilerExportCSV, "Exports the current profiler data.",
"profiler_exportcsv <output file>" },
2015-05-19 04:53:37 +02:00
};
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandWindows(InteractiveConsole& console, [[maybe_unused]] const arguments_t& argv)
{
2018-01-06 00:28:04 +01:00
for (auto s : console_window_table)
{
2018-03-11 20:46:03 +01:00
console.WriteLine(s);
2018-01-06 00:28:04 +01:00
}
return 0;
}
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandVariables(InteractiveConsole& console, [[maybe_unused]] const arguments_t& argv)
{
2018-01-06 00:28:04 +01:00
for (auto s : console_variable_table)
{
2018-03-11 20:46:03 +01:00
console.WriteLine(s);
2018-01-06 00:28:04 +01:00
}
return 0;
}
2023-01-16 21:14:50 +01:00
static int32_t ConsoleCommandHelp(InteractiveConsole& console, const arguments_t& argv)
2015-05-19 19:46:05 +02:00
{
if (!argv.empty())
2018-01-06 00:28:04 +01:00
{
2018-06-22 22:59:46 +02:00
for (const auto& c : console_command_table)
2018-01-06 00:28:04 +01:00
{
if (argv[0] == c.command)
2018-01-06 00:28:04 +01:00
{
2018-03-11 20:46:03 +01:00
console.WriteLine(c.help);
console.WriteFormatLine("\nUsage: %s", c.usage);
}
}
}
2018-01-06 00:28:04 +01:00
else
{
2023-01-16 21:14:50 +01:00
ConsoleWriteAllCommands(console);
}
return 0;
2015-05-19 19:46:05 +02:00
}
2023-01-16 21:14:50 +01:00
static void ConsoleWriteAllCommands(InteractiveConsole& console)
2015-05-19 04:53:37 +02:00
{
2018-06-22 22:59:46 +02:00
for (const auto& c : console_command_table)
2018-01-06 00:28:04 +01:00
{
2018-03-11 20:46:03 +01:00
console.WriteLine(c.command);
2018-01-06 00:28:04 +01:00
}
2015-05-19 04:53:37 +02:00
}
2023-01-16 21:14:50 +01:00
static bool InvalidArguments(bool* invalid, bool arguments)
2015-05-19 04:53:37 +02:00
{
2018-06-22 22:59:46 +02:00
if (!arguments)
{
2018-03-11 20:46:03 +01:00
*invalid = true;
return false;
}
return true;
}
2015-05-19 04:53:37 +02:00
2018-06-22 22:59:46 +02:00
void InteractiveConsole::Execute(const std::string& s)
{
arguments_t argv;
argv.reserve(8);
2018-06-22 22:59:46 +02:00
const utf8* start = s.c_str();
const utf8* end;
bool inQuotes = false;
2018-06-22 22:59:46 +02:00
do
{
while (*start == ' ')
start++;
2018-06-22 22:59:46 +02:00
if (*start == '"')
{
inQuotes = true;
start++;
}
2018-06-22 22:59:46 +02:00
else
{
inQuotes = false;
}
end = start;
2018-06-22 22:59:46 +02:00
while (*end != 0)
{
if (*end == ' ' && !inQuotes)
break;
if (*end == '"' && inQuotes)
break;
end++;
}
size_t length = end - start;
2018-06-22 22:59:46 +02:00
if (length > 0)
{
argv.emplace_back(start, length);
}
start = end;
} while (*end != 0);
if (argv.empty())
return;
bool validCommand = false;
for (const auto& c : console_command_table)
2018-01-06 00:28:04 +01:00
{
if (argv[0] == c.command)
2018-01-06 00:28:04 +01:00
{
argv.erase(argv.begin());
c.func(*this, argv);
validCommand = true;
break;
}
}
if (!validCommand)
2018-06-22 22:59:46 +02:00
{
2018-03-11 20:46:03 +01:00
WriteLineError("Unknown command. Type help to list available commands.");
}
2015-06-19 16:51:54 +02:00
}
2018-06-22 22:59:46 +02:00
void InteractiveConsole::WriteLine(const std::string& s)
2015-06-19 16:51:54 +02:00
{
2020-10-16 01:13:52 +02:00
WriteLine(s, FormatToken::ColourWindow2);
2015-07-16 10:21:10 +02:00
}
2018-06-22 22:59:46 +02:00
void InteractiveConsole::WriteLineError(const std::string& s)
{
2020-10-16 01:13:52 +02:00
WriteLine(s, FormatToken::ColourRed);
2018-03-11 20:46:03 +01:00
}
2018-06-22 22:59:46 +02:00
void InteractiveConsole::WriteLineWarning(const std::string& s)
2018-03-11 20:46:03 +01:00
{
2020-10-16 01:13:52 +02:00
WriteLine(s, FormatToken::ColourYellow);
2018-03-11 20:46:03 +01:00
}
2018-06-22 22:59:46 +02:00
void InteractiveConsole::WriteFormatLine(const char* format, ...)
2018-03-11 20:46:03 +01:00
{
va_list list;
va_start(list, format);
2018-03-12 18:39:58 +01:00
auto buffer = String::Format_VA(format, list);
2018-03-11 20:46:03 +01:00
va_end(list);
WriteLine(buffer);
2018-03-11 20:46:03 +01:00
}