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>
This commit is contained in:
Adam 2020-12-09 23:39:10 -07:00 committed by GitHub
parent 5a849ededf
commit f09b14ef2b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
283 changed files with 18390 additions and 16474 deletions

View File

@ -6,7 +6,7 @@
"/usr/include",
"/usr/local/include",
"${workspaceRoot}",
"${workspaceRoot}/src"
"${workspaceRoot}/src/**"
],
"defines": [],
"intelliSenseMode": "clang-x64",

File diff suppressed because it is too large Load Diff

View File

@ -18,8 +18,8 @@
#include <openrct2/Game.h>
#include <openrct2/Input.h>
#include <openrct2/OpenRCT2.h>
#include <openrct2/actions/LoadOrQuitAction.hpp>
#include <openrct2/actions/SetCheatAction.hpp>
#include <openrct2/actions/LoadOrQuitAction.h>
#include <openrct2/actions/SetCheatAction.h>
#include <openrct2/audio/audio.h>
#include <openrct2/config/Config.h>
#include <openrct2/interface/Chat.h>

View File

@ -18,12 +18,12 @@
#include <openrct2/GameState.h>
#include <openrct2/Input.h>
#include <openrct2/OpenRCT2.h>
#include <openrct2/actions/BalloonPressAction.hpp>
#include <openrct2/actions/FootpathAdditionRemoveAction.hpp>
#include <openrct2/actions/LargeSceneryRemoveAction.hpp>
#include <openrct2/actions/ParkEntranceRemoveAction.hpp>
#include <openrct2/actions/SmallSceneryRemoveAction.hpp>
#include <openrct2/actions/WallRemoveAction.hpp>
#include <openrct2/actions/BalloonPressAction.h>
#include <openrct2/actions/FootpathAdditionRemoveAction.h>
#include <openrct2/actions/LargeSceneryRemoveAction.h>
#include <openrct2/actions/ParkEntranceRemoveAction.h>
#include <openrct2/actions/SmallSceneryRemoveAction.h>
#include <openrct2/actions/WallRemoveAction.h>
#include <openrct2/localisation/Localisation.h>
#include <openrct2/ride/Ride.h>
#include <openrct2/ride/RideData.h>

View File

@ -12,10 +12,10 @@
#include <openrct2-ui/interface/Widget.h>
#include <openrct2-ui/windows/Window.h>
#include <openrct2/Game.h>
#include <openrct2/actions/BannerRemoveAction.hpp>
#include <openrct2/actions/BannerSetColourAction.hpp>
#include <openrct2/actions/BannerSetNameAction.hpp>
#include <openrct2/actions/BannerSetStyleAction.hpp>
#include <openrct2/actions/BannerRemoveAction.h>
#include <openrct2/actions/BannerSetColourAction.h>
#include <openrct2/actions/BannerSetNameAction.h>
#include <openrct2/actions/BannerSetStyleAction.h>
#include <openrct2/config/Config.h>
#include <openrct2/localisation/Localisation.h>
#include <openrct2/sprites.h>

View File

@ -15,8 +15,8 @@
#include <openrct2/Context.h>
#include <openrct2/Game.h>
#include <openrct2/OpenRCT2.h>
#include <openrct2/actions/ParkSetDateAction.hpp>
#include <openrct2/actions/SetCheatAction.hpp>
#include <openrct2/actions/ParkSetDateAction.h>
#include <openrct2/actions/SetCheatAction.h>
#include <openrct2/config/Config.h>
#include <openrct2/localisation/Date.h>
#include <openrct2/localisation/Localisation.h>

View File

@ -17,7 +17,7 @@
#include <openrct2/EditorObjectSelectionSession.h>
#include <openrct2/Game.h>
#include <openrct2/OpenRCT2.h>
#include <openrct2/actions/LoadOrQuitAction.hpp>
#include <openrct2/actions/LoadOrQuitAction.h>
#include <openrct2/audio/audio.h>
#include <openrct2/config/Config.h>
#include <openrct2/core/String.hpp>

View File

@ -17,7 +17,7 @@
#include <openrct2/Context.h>
#include <openrct2/GameState.h>
#include <openrct2/OpenRCT2.h>
#include <openrct2/actions/ParkSetNameAction.hpp>
#include <openrct2/actions/ParkSetNameAction.h>
#include <openrct2/core/String.hpp>
#include <openrct2/drawing/Drawing.h>
#include <openrct2/drawing/Font.h>

View File

@ -17,8 +17,8 @@
#include <openrct2/Editor.h>
#include <openrct2/Game.h>
#include <openrct2/OpenRCT2.h>
#include <openrct2/actions/ClimateSetAction.hpp>
#include <openrct2/actions/ScenarioSetSettingAction.hpp>
#include <openrct2/actions/ClimateSetAction.h>
#include <openrct2/actions/ScenarioSetSettingAction.h>
#include <openrct2/drawing/Drawing.h>
#include <openrct2/interface/Colour.h>
#include <openrct2/localisation/StringIds.h>

View File

@ -15,8 +15,8 @@
#include <openrct2/Context.h>
#include <openrct2/Game.h>
#include <openrct2/GameState.h>
#include <openrct2/actions/ParkSetLoanAction.hpp>
#include <openrct2/actions/ParkSetResearchFundingAction.hpp>
#include <openrct2/actions/ParkSetLoanAction.h>
#include <openrct2/actions/ParkSetResearchFundingAction.h>
#include <openrct2/config/Config.h>
#include <openrct2/localisation/Date.h>
#include <openrct2/localisation/Localisation.h>

View File

@ -15,7 +15,7 @@
#include <openrct2/Game.h>
#include <openrct2/Input.h>
#include <openrct2/OpenRCT2.h>
#include <openrct2/actions/FootpathPlaceAction.hpp>
#include <openrct2/actions/FootpathPlaceAction.h>
#include <openrct2/audio/audio.h>
#include <openrct2/localisation/Localisation.h>
#include <openrct2/object/ObjectLimits.h>

View File

@ -14,8 +14,8 @@
#include <openrct2/Game.h>
#include <openrct2/GameState.h>
#include <openrct2/Input.h>
#include <openrct2/actions/GuestSetFlagsAction.hpp>
#include <openrct2/actions/PeepPickupAction.hpp>
#include <openrct2/actions/GuestSetFlagsAction.h>
#include <openrct2/actions/PeepPickupAction.h>
#include <openrct2/config/Config.h>
#include <openrct2/localisation/Localisation.h>
#include <openrct2/management/Marketing.h>

View File

@ -15,7 +15,7 @@
#include <openrct2/Context.h>
#include <openrct2/Game.h>
#include <openrct2/Input.h>
#include <openrct2/actions/LandBuyRightsAction.hpp>
#include <openrct2/actions/LandBuyRightsAction.h>
#include <openrct2/drawing/Drawing.h>
#include <openrct2/localisation/Localisation.h>
#include <openrct2/world/Park.h>

View File

@ -18,12 +18,13 @@
#include <openrct2/Game.h>
#include <openrct2/Input.h>
#include <openrct2/OpenRCT2.h>
#include <openrct2/actions/LandSetRightsAction.hpp>
#include <openrct2/actions/PlaceParkEntranceAction.hpp>
#include <openrct2/actions/PlacePeepSpawnAction.hpp>
#include <openrct2/actions/SurfaceSetStyleAction.hpp>
#include <openrct2/actions/LandSetRightsAction.h>
#include <openrct2/actions/PlaceParkEntranceAction.h>
#include <openrct2/actions/PlacePeepSpawnAction.h>
#include <openrct2/actions/SurfaceSetStyleAction.h>
#include <openrct2/audio/audio.h>
#include <openrct2/localisation/Localisation.h>
#include <openrct2/ride/RideData.h>
#include <openrct2/ride/Track.h>
#include <openrct2/world/Entrance.h>
#include <openrct2/world/Footpath.h>

View File

@ -13,7 +13,7 @@
#include <openrct2/Context.h>
#include <openrct2/Game.h>
#include <openrct2/Input.h>
#include <openrct2/actions/RideEntranceExitPlaceAction.hpp>
#include <openrct2/actions/RideEntranceExitPlaceAction.h>
#include <openrct2/audio/audio.h>
#include <openrct2/drawing/Drawing.h>
#include <openrct2/localisation/Localisation.h>

View File

@ -11,7 +11,7 @@
#include <openrct2-ui/interface/Widget.h>
#include <openrct2-ui/windows/Window.h>
#include <openrct2/Game.h>
#include <openrct2/actions/NetworkModifyGroupAction.hpp>
#include <openrct2/actions/NetworkModifyGroupAction.h>
#include <openrct2/config/Config.h>
#include <openrct2/drawing/Drawing.h>
#include <openrct2/localisation/Localisation.h>

View File

@ -12,7 +12,7 @@
#include <openrct2-ui/interface/Widget.h>
#include <openrct2-ui/windows/Window.h>
#include <openrct2/Game.h>
#include <openrct2/actions/ParkMarketingAction.hpp>
#include <openrct2/actions/ParkMarketingAction.h>
#include <openrct2/config/Config.h>
#include <openrct2/drawing/Drawing.h>
#include <openrct2/localisation/Localisation.h>

View File

@ -21,7 +21,7 @@
#include <openrct2/Game.h>
#include <openrct2/GameState.h>
#include <openrct2/Input.h>
#include <openrct2/actions/ParkSetNameAction.hpp>
#include <openrct2/actions/ParkSetNameAction.h>
#include <openrct2/config/Config.h>
#include <openrct2/localisation/Date.h>
#include <openrct2/localisation/Localisation.h>

View File

@ -13,8 +13,8 @@
#include <openrct2-ui/windows/Window.h>
#include <openrct2/Game.h>
#include <openrct2/Input.h>
#include <openrct2/actions/PlayerKickAction.hpp>
#include <openrct2/actions/PlayerSetGroupAction.hpp>
#include <openrct2/actions/PlayerKickAction.h>
#include <openrct2/actions/PlayerSetGroupAction.h>
#include <openrct2/config/Config.h>
#include <openrct2/drawing/Drawing.h>
#include <openrct2/interface/Colour.h>

View File

@ -11,7 +11,7 @@
#include <openrct2-ui/interface/Widget.h>
#include <openrct2-ui/windows/Window.h>
#include <openrct2/Game.h>
#include <openrct2/actions/ParkSetResearchFundingAction.hpp>
#include <openrct2/actions/ParkSetResearchFundingAction.h>
#include <openrct2/localisation/Localisation.h>
#include <openrct2/management/Finance.h>
#include <openrct2/management/NewsItem.h>

View File

@ -23,11 +23,11 @@
#include <openrct2/Input.h>
#include <openrct2/OpenRCT2.h>
#include <openrct2/actions/GameAction.h>
#include <openrct2/actions/ParkSetParameterAction.hpp>
#include <openrct2/actions/RideSetAppearanceAction.hpp>
#include <openrct2/actions/RideSetColourScheme.hpp>
#include <openrct2/actions/RideSetPriceAction.hpp>
#include <openrct2/actions/RideSetSetting.hpp>
#include <openrct2/actions/ParkSetParameterAction.h>
#include <openrct2/actions/RideSetAppearanceAction.h>
#include <openrct2/actions/RideSetColourSchemeAction.h>
#include <openrct2/actions/RideSetPriceAction.h>
#include <openrct2/actions/RideSetSettingAction.h>
#include <openrct2/audio/audio.h>
#include <openrct2/config/Config.h>
#include <openrct2/core/String.hpp>

View File

@ -17,10 +17,10 @@
#include <openrct2/Context.h>
#include <openrct2/Game.h>
#include <openrct2/Input.h>
#include <openrct2/actions/RideEntranceExitPlaceAction.hpp>
#include <openrct2/actions/TrackPlaceAction.hpp>
#include <openrct2/actions/TrackRemoveAction.hpp>
#include <openrct2/actions/TrackSetBrakeSpeedAction.hpp>
#include <openrct2/actions/RideEntranceExitPlaceAction.h>
#include <openrct2/actions/TrackPlaceAction.h>
#include <openrct2/actions/TrackRemoveAction.h>
#include <openrct2/actions/TrackSetBrakeSpeedAction.h>
#include <openrct2/audio/audio.h>
#include <openrct2/config/Config.h>
#include <openrct2/localisation/Localisation.h>

View File

@ -12,10 +12,10 @@
#include <openrct2-ui/interface/Widget.h>
#include <openrct2-ui/windows/Window.h>
#include <openrct2/Game.h>
#include <openrct2/actions/LargeSceneryRemoveAction.hpp>
#include <openrct2/actions/SignSetNameAction.hpp>
#include <openrct2/actions/SignSetStyleAction.hpp>
#include <openrct2/actions/WallRemoveAction.hpp>
#include <openrct2/actions/LargeSceneryRemoveAction.h>
#include <openrct2/actions/SignSetNameAction.h>
#include <openrct2/actions/SignSetStyleAction.h>
#include <openrct2/actions/WallRemoveAction.h>
#include <openrct2/config/Config.h>
#include <openrct2/localisation/Localisation.h>
#include <openrct2/localisation/StringIds.h>

View File

@ -16,10 +16,10 @@
#include <openrct2/Context.h>
#include <openrct2/Game.h>
#include <openrct2/Input.h>
#include <openrct2/actions/PeepPickupAction.hpp>
#include <openrct2/actions/StaffSetCostumeAction.hpp>
#include <openrct2/actions/StaffSetOrdersAction.hpp>
#include <openrct2/actions/StaffSetPatrolAreaAction.hpp>
#include <openrct2/actions/PeepPickupAction.h>
#include <openrct2/actions/StaffSetCostumeAction.h>
#include <openrct2/actions/StaffSetOrdersAction.h>
#include <openrct2/actions/StaffSetPatrolAreaAction.h>
#include <openrct2/config/Config.h>
#include <openrct2/localisation/Localisation.h>
#include <openrct2/management/Finance.h>

View File

@ -10,7 +10,7 @@
#include <openrct2-ui/interface/Widget.h>
#include <openrct2-ui/windows/Window.h>
#include <openrct2/Game.h>
#include <openrct2/actions/StaffFireAction.hpp>
#include <openrct2/actions/StaffFireAction.h>
#include <openrct2/drawing/Drawing.h>
#include <openrct2/interface/Colour.h>
#include <openrct2/localisation/Localisation.h>

View File

@ -15,8 +15,8 @@
#include <openrct2/Context.h>
#include <openrct2/Game.h>
#include <openrct2/Input.h>
#include <openrct2/actions/StaffFireAction.hpp>
#include <openrct2/actions/StaffSetColourAction.hpp>
#include <openrct2/actions/StaffFireAction.h>
#include <openrct2/actions/StaffSetColourAction.h>
#include <openrct2/config/Config.h>
#include <openrct2/drawing/Drawing.h>
#include <openrct2/localisation/Localisation.h>

View File

@ -14,7 +14,7 @@
#include <openrct2-ui/windows/Window.h>
#include <openrct2/Game.h>
#include <openrct2/Input.h>
#include <openrct2/actions/TileModifyAction.hpp>
#include <openrct2/actions/TileModifyAction.h>
#include <openrct2/common.h>
#include <openrct2/core/Guard.hpp>
#include <openrct2/localisation/Localisation.h>

View File

@ -16,7 +16,7 @@
#include <openrct2/Input.h>
#include <openrct2/ParkImporter.h>
#include <openrct2/PlatformEnvironment.h>
#include <openrct2/actions/LoadOrQuitAction.hpp>
#include <openrct2/actions/LoadOrQuitAction.h>
#include <openrct2/config/Config.h>
#include <openrct2/localisation/Localisation.h>
#include <openrct2/sprites.h>

View File

@ -26,25 +26,25 @@
#include <openrct2/Input.h>
#include <openrct2/OpenRCT2.h>
#include <openrct2/ParkImporter.h>
#include <openrct2/actions/BannerPlaceAction.hpp>
#include <openrct2/actions/BannerSetColourAction.hpp>
#include <openrct2/actions/ClearAction.hpp>
#include <openrct2/actions/FootpathAdditionPlaceAction.hpp>
#include <openrct2/actions/LandLowerAction.hpp>
#include <openrct2/actions/LandRaiseAction.hpp>
#include <openrct2/actions/LandSmoothAction.hpp>
#include <openrct2/actions/LargeSceneryPlaceAction.hpp>
#include <openrct2/actions/LargeScenerySetColourAction.hpp>
#include <openrct2/actions/LoadOrQuitAction.hpp>
#include <openrct2/actions/PauseToggleAction.hpp>
#include <openrct2/actions/SetCheatAction.hpp>
#include <openrct2/actions/SmallSceneryPlaceAction.hpp>
#include <openrct2/actions/SmallScenerySetColourAction.hpp>
#include <openrct2/actions/SurfaceSetStyleAction.hpp>
#include <openrct2/actions/WallPlaceAction.hpp>
#include <openrct2/actions/WallSetColourAction.hpp>
#include <openrct2/actions/WaterLowerAction.hpp>
#include <openrct2/actions/WaterRaiseAction.hpp>
#include <openrct2/actions/BannerPlaceAction.h>
#include <openrct2/actions/BannerSetColourAction.h>
#include <openrct2/actions/ClearAction.h>
#include <openrct2/actions/FootpathAdditionPlaceAction.h>
#include <openrct2/actions/LandLowerAction.h>
#include <openrct2/actions/LandRaiseAction.h>
#include <openrct2/actions/LandSmoothAction.h>
#include <openrct2/actions/LargeSceneryPlaceAction.h>
#include <openrct2/actions/LargeScenerySetColourAction.h>
#include <openrct2/actions/LoadOrQuitAction.h>
#include <openrct2/actions/PauseToggleAction.h>
#include <openrct2/actions/SetCheatAction.h>
#include <openrct2/actions/SmallSceneryPlaceAction.h>
#include <openrct2/actions/SmallScenerySetColourAction.h>
#include <openrct2/actions/SurfaceSetStyleAction.h>
#include <openrct2/actions/WallPlaceAction.h>
#include <openrct2/actions/WallSetColourAction.h>
#include <openrct2/actions/WaterLowerAction.h>
#include <openrct2/actions/WaterRaiseAction.h>
#include <openrct2/audio/audio.h>
#include <openrct2/config/Config.h>
#include <openrct2/interface/Chat.h>

View File

@ -10,8 +10,8 @@
#include "Cheats.h"
#include "GameState.h"
#include "actions/ParkSetLoanAction.hpp"
#include "actions/SetCheatAction.hpp"
#include "actions/ParkSetLoanAction.h"
#include "actions/SetCheatAction.h"
#include "config/Config.h"
#include "core/DataSerialiser.h"
#include "localisation/Localisation.h"

View File

@ -16,8 +16,8 @@
#include "GameState.h"
#include "OpenRCT2.h"
#include "ParkImporter.h"
#include "actions/LandBuyRightsAction.hpp"
#include "actions/LandSetRightsAction.hpp"
#include "actions/LandBuyRightsAction.h"
#include "actions/LandSetRightsAction.h"
#include "audio/audio.h"
#include "interface/Viewport.h"
#include "interface/Window_internal.h"

View File

@ -19,7 +19,7 @@
#include "ParkImporter.h"
#include "PlatformEnvironment.h"
#include "ReplayManager.h"
#include "actions/LoadOrQuitAction.hpp"
#include "actions/LoadOrQuitAction.h"
#include "audio/audio.h"
#include "config/Config.h"
#include "core/FileScanner.h"

View File

@ -15,13 +15,13 @@
#include "OpenRCT2.h"
#include "ParkImporter.h"
#include "PlatformEnvironment.h"
#include "actions/FootpathPlaceAction.hpp"
#include "actions/FootpathPlaceAction.h"
#include "actions/GameAction.h"
#include "actions/RideEntranceExitPlaceAction.hpp"
#include "actions/RideSetSetting.hpp"
#include "actions/SetCheatAction.hpp"
#include "actions/TileModifyAction.hpp"
#include "actions/TrackPlaceAction.hpp"
#include "actions/RideEntranceExitPlaceAction.h"
#include "actions/RideSetSettingAction.h"
#include "actions/SetCheatAction.h"
#include "actions/TileModifyAction.h"
#include "actions/TrackPlaceAction.h"
#include "config/Config.h"
#include "core/DataSerialiser.h"
#include "core/Path.hpp"

View File

@ -0,0 +1,48 @@
/*****************************************************************************
* 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.
*****************************************************************************/
#include "BalloonPressAction.h"
#include "GameAction.h"
void BalloonPressAction::AcceptParameters(GameActionParameterVisitor& visitor)
{
visitor.Visit("id", _spriteIndex);
}
void BalloonPressAction::Serialise(DataSerialiser& stream)
{
GameAction::Serialise(stream);
stream << DS_TAG(_spriteIndex);
}
GameActions::Result::Ptr BalloonPressAction::Query() const
{
auto balloon = TryGetEntity<Balloon>(_spriteIndex);
if (balloon == nullptr)
{
log_error("Tried getting invalid sprite for balloon: %u", _spriteIndex);
return MakeResult(GameActions::Status::InvalidParameters, STR_NONE);
}
return MakeResult();
}
GameActions::Result::Ptr BalloonPressAction::Execute() const
{
auto balloon = TryGetEntity<Balloon>(_spriteIndex);
if (balloon == nullptr)
{
log_error("Tried getting invalid sprite for balloon: %u", _spriteIndex);
return MakeResult(GameActions::Status::InvalidParameters, STR_NONE);
}
balloon->Press();
return MakeResult();
}

View File

@ -0,0 +1,35 @@
/*****************************************************************************
* 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
#include "../world/Sprite.h"
#include "GameAction.h"
DEFINE_GAME_ACTION(BalloonPressAction, GAME_COMMAND_BALLOON_PRESS, GameActions::Result)
{
uint16_t _spriteIndex{ SPRITE_INDEX_NULL };
public:
BalloonPressAction() = default;
BalloonPressAction(uint16_t spriteIndex)
: _spriteIndex(spriteIndex)
{
}
void AcceptParameters(GameActionParameterVisitor & visitor) override;
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags();
}
void Serialise(DataSerialiser & stream) override;
GameActions::Result::Ptr Query() const override;
GameActions::Result::Ptr Execute() const override;
};

View File

@ -1,66 +0,0 @@
/*****************************************************************************
* 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
#include "../world/Sprite.h"
#include "GameAction.h"
DEFINE_GAME_ACTION(BalloonPressAction, GAME_COMMAND_BALLOON_PRESS, GameActions::Result)
{
uint16_t _spriteIndex{ SPRITE_INDEX_NULL };
public:
BalloonPressAction() = default;
BalloonPressAction(uint16_t spriteIndex)
: _spriteIndex(spriteIndex)
{
}
void AcceptParameters(GameActionParameterVisitor & visitor) override
{
visitor.Visit("id", _spriteIndex);
}
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags();
}
void Serialise(DataSerialiser & stream) override
{
GameAction::Serialise(stream);
stream << DS_TAG(_spriteIndex);
}
GameActions::Result::Ptr Query() const override
{
auto balloon = TryGetEntity<Balloon>(_spriteIndex);
if (balloon == nullptr)
{
log_error("Tried getting invalid sprite for balloon: %u", _spriteIndex);
return MakeResult(GameActions::Status::InvalidParameters, STR_NONE);
}
return MakeResult();
}
GameActions::Result::Ptr Execute() const override
{
auto balloon = TryGetEntity<Balloon>(_spriteIndex);
if (balloon == nullptr)
{
log_error("Tried getting invalid sprite for balloon: %u", _spriteIndex);
return MakeResult(GameActions::Status::InvalidParameters, STR_NONE);
}
balloon->Press();
return MakeResult();
}
};

View File

@ -0,0 +1,182 @@
/*****************************************************************************
* 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.
*****************************************************************************/
#include "BannerPlaceAction.h"
#include "../management/Finance.h"
#include "../world/Banner.h"
#include "../world/MapAnimation.h"
#include "../world/Scenery.h"
#include "GameAction.h"
void BannerPlaceAction::AcceptParameters(GameActionParameterVisitor& visitor)
{
visitor.Visit(_loc);
visitor.Visit("object", _bannerType);
visitor.Visit("primaryColour", _primaryColour);
_bannerIndex = create_new_banner(0);
}
void BannerPlaceAction::Serialise(DataSerialiser& stream)
{
GameAction::Serialise(stream);
stream << DS_TAG(_loc) << DS_TAG(_bannerType) << DS_TAG(_bannerIndex) << DS_TAG(_primaryColour);
}
GameActions::Result::Ptr BannerPlaceAction::Query() const
{
auto res = MakeResult();
res->Position.x = _loc.x + 16;
res->Position.y = _loc.y + 16;
res->Position.z = _loc.z;
res->Expenditure = ExpenditureType::Landscaping;
res->ErrorTitle = STR_CANT_POSITION_THIS_HERE;
if (!map_check_free_elements_and_reorganise(1))
{
log_error("No free map elements.");
return MakeResult(GameActions::Status::NoFreeElements, STR_CANT_POSITION_THIS_HERE);
}
if (!LocationValid(_loc))
{
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE);
}
auto pathElement = GetValidPathElement();
if (pathElement == nullptr)
{
return MakeResult(
GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE, STR_CAN_ONLY_BE_BUILT_ACROSS_PATHS);
}
if (!map_can_build_at(_loc))
{
return MakeResult(GameActions::Status::NotOwned, STR_CANT_POSITION_THIS_HERE, STR_LAND_NOT_OWNED_BY_PARK);
}
auto baseHeight = _loc.z + PATH_HEIGHT_STEP;
BannerElement* existingBannerElement = map_get_banner_element_at({ _loc.x, _loc.y, baseHeight }, _loc.direction);
if (existingBannerElement != nullptr)
{
return MakeResult(GameActions::Status::ItemAlreadyPlaced, STR_CANT_POSITION_THIS_HERE, STR_BANNER_SIGN_IN_THE_WAY);
}
if (_bannerIndex == BANNER_INDEX_NULL || _bannerIndex >= MAX_BANNERS)
{
log_error("Invalid banner index, bannerIndex = %u", _bannerIndex);
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE);
}
auto banner = GetBanner(_bannerIndex);
if (!banner->IsNull())
{
log_error("Banner index in use, bannerIndex = %u", _bannerIndex);
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE);
}
rct_scenery_entry* bannerEntry = get_banner_entry(_bannerType);
if (bannerEntry == nullptr)
{
log_error("Invalid banner object type. bannerType = ", _bannerType);
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE);
}
res->Cost = bannerEntry->banner.price;
return res;
}
GameActions::Result::Ptr BannerPlaceAction::Execute() const
{
auto res = MakeResult();
res->Position.x = _loc.x + 16;
res->Position.y = _loc.y + 16;
res->Position.z = _loc.z;
res->Expenditure = ExpenditureType::Landscaping;
res->ErrorTitle = STR_CANT_POSITION_THIS_HERE;
if (!map_check_free_elements_and_reorganise(1))
{
log_error("No free map elements.");
return MakeResult(GameActions::Status::NoFreeElements, STR_CANT_POSITION_THIS_HERE);
}
if (_bannerIndex == BANNER_INDEX_NULL || _bannerIndex >= MAX_BANNERS)
{
log_error("Invalid banner index, bannerIndex = %u", _bannerIndex);
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE);
}
rct_scenery_entry* bannerEntry = get_banner_entry(_bannerType);
if (bannerEntry == nullptr)
{
log_error("Invalid banner object type. bannerType = ", _bannerType);
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE);
}
auto banner = GetBanner(_bannerIndex);
if (!banner->IsNull())
{
log_error("Banner index in use, bannerIndex = %u", _bannerIndex);
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE);
}
TileElement* newTileElement = tile_element_insert({ _loc, _loc.z + (2 * COORDS_Z_STEP) }, 0b0000);
assert(newTileElement != nullptr);
banner->flags = 0;
banner->text = {};
banner->text_colour = 2;
banner->type = _bannerType; // Banner must be deleted after this point in an early return
banner->colour = _primaryColour;
banner->position = TileCoordsXY(_loc);
newTileElement->SetType(TILE_ELEMENT_TYPE_BANNER);
BannerElement* bannerElement = newTileElement->AsBanner();
bannerElement->SetClearanceZ(_loc.z + PATH_CLEARANCE);
bannerElement->SetPosition(_loc.direction);
bannerElement->ResetAllowedEdges();
bannerElement->SetIndex(_bannerIndex);
if (GetFlags() & GAME_COMMAND_FLAG_GHOST)
{
bannerElement->SetGhost(true);
}
map_invalidate_tile_full(_loc);
map_animation_create(MAP_ANIMATION_TYPE_BANNER, CoordsXYZ{ _loc, bannerElement->GetBaseZ() });
res->Cost = bannerEntry->banner.price;
return res;
}
PathElement* BannerPlaceAction::GetValidPathElement() const
{
TileElement* tileElement = map_get_first_element_at(_loc);
do
{
if (tileElement == nullptr)
break;
if (tileElement->GetType() != TILE_ELEMENT_TYPE_PATH)
continue;
auto pathElement = tileElement->AsPath();
if (pathElement->GetBaseZ() != _loc.z && pathElement->GetBaseZ() != _loc.z - PATH_HEIGHT_STEP)
continue;
if (!(pathElement->GetEdges() & (1 << _loc.direction)))
continue;
if (pathElement->IsGhost() && !(GetFlags() & GAME_COMMAND_FLAG_GHOST))
continue;
return pathElement;
} while (!(tileElement++)->IsLastForTile());
return nullptr;
}

View File

@ -0,0 +1,45 @@
/*****************************************************************************
* 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
#include "GameAction.h"
DEFINE_GAME_ACTION(BannerPlaceAction, GAME_COMMAND_PLACE_BANNER, GameActions::Result)
{
private:
CoordsXYZD _loc;
ObjectEntryIndex _bannerType{ BANNER_NULL };
BannerIndex _bannerIndex{ BANNER_INDEX_NULL };
uint8_t _primaryColour{};
public:
BannerPlaceAction() = default;
BannerPlaceAction(const CoordsXYZD& loc, uint8_t bannerType, BannerIndex bannerIndex, uint8_t primaryColour)
: _loc(loc)
, _bannerType(bannerType)
, _bannerIndex(bannerIndex)
, _primaryColour(primaryColour)
{
}
void AcceptParameters(GameActionParameterVisitor & visitor) override;
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags();
}
void Serialise(DataSerialiser & stream) override;
GameActions::Result::Ptr Query() const override;
GameActions::Result::Ptr Execute() const override;
private:
PathElement* GetValidPathElement() const;
};

View File

@ -1,207 +0,0 @@
/*****************************************************************************
* 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
#include "../management/Finance.h"
#include "../world/Banner.h"
#include "../world/MapAnimation.h"
#include "../world/Scenery.h"
#include "GameAction.h"
DEFINE_GAME_ACTION(BannerPlaceAction, GAME_COMMAND_PLACE_BANNER, GameActions::Result)
{
private:
CoordsXYZD _loc;
ObjectEntryIndex _bannerType{ BANNER_NULL };
BannerIndex _bannerIndex{ BANNER_INDEX_NULL };
uint8_t _primaryColour{};
public:
BannerPlaceAction() = default;
BannerPlaceAction(const CoordsXYZD& loc, uint8_t bannerType, BannerIndex bannerIndex, uint8_t primaryColour)
: _loc(loc)
, _bannerType(bannerType)
, _bannerIndex(bannerIndex)
, _primaryColour(primaryColour)
{
}
void AcceptParameters(GameActionParameterVisitor & visitor) override
{
visitor.Visit(_loc);
visitor.Visit("object", _bannerType);
visitor.Visit("primaryColour", _primaryColour);
_bannerIndex = create_new_banner(0);
}
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags();
}
void Serialise(DataSerialiser & stream) override
{
GameAction::Serialise(stream);
stream << DS_TAG(_loc) << DS_TAG(_bannerType) << DS_TAG(_bannerIndex) << DS_TAG(_primaryColour);
}
GameActions::Result::Ptr Query() const override
{
auto res = MakeResult();
res->Position.x = _loc.x + 16;
res->Position.y = _loc.y + 16;
res->Position.z = _loc.z;
res->Expenditure = ExpenditureType::Landscaping;
res->ErrorTitle = STR_CANT_POSITION_THIS_HERE;
if (!map_check_free_elements_and_reorganise(1))
{
log_error("No free map elements.");
return MakeResult(GameActions::Status::NoFreeElements, STR_CANT_POSITION_THIS_HERE);
}
if (!LocationValid(_loc))
{
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE);
}
auto pathElement = GetValidPathElement();
if (pathElement == nullptr)
{
return MakeResult(
GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE, STR_CAN_ONLY_BE_BUILT_ACROSS_PATHS);
}
if (!map_can_build_at(_loc))
{
return MakeResult(GameActions::Status::NotOwned, STR_CANT_POSITION_THIS_HERE, STR_LAND_NOT_OWNED_BY_PARK);
}
auto baseHeight = _loc.z + PATH_HEIGHT_STEP;
BannerElement* existingBannerElement = map_get_banner_element_at({ _loc.x, _loc.y, baseHeight }, _loc.direction);
if (existingBannerElement != nullptr)
{
return MakeResult(GameActions::Status::ItemAlreadyPlaced, STR_CANT_POSITION_THIS_HERE, STR_BANNER_SIGN_IN_THE_WAY);
}
if (_bannerIndex == BANNER_INDEX_NULL || _bannerIndex >= MAX_BANNERS)
{
log_error("Invalid banner index, bannerIndex = %u", _bannerIndex);
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE);
}
auto banner = GetBanner(_bannerIndex);
if (!banner->IsNull())
{
log_error("Banner index in use, bannerIndex = %u", _bannerIndex);
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE);
}
rct_scenery_entry* bannerEntry = get_banner_entry(_bannerType);
if (bannerEntry == nullptr)
{
log_error("Invalid banner object type. bannerType = ", _bannerType);
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE);
}
res->Cost = bannerEntry->banner.price;
return res;
}
GameActions::Result::Ptr Execute() const override
{
auto res = MakeResult();
res->Position.x = _loc.x + 16;
res->Position.y = _loc.y + 16;
res->Position.z = _loc.z;
res->Expenditure = ExpenditureType::Landscaping;
res->ErrorTitle = STR_CANT_POSITION_THIS_HERE;
if (!map_check_free_elements_and_reorganise(1))
{
log_error("No free map elements.");
return MakeResult(GameActions::Status::NoFreeElements, STR_CANT_POSITION_THIS_HERE);
}
if (_bannerIndex == BANNER_INDEX_NULL || _bannerIndex >= MAX_BANNERS)
{
log_error("Invalid banner index, bannerIndex = %u", _bannerIndex);
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE);
}
rct_scenery_entry* bannerEntry = get_banner_entry(_bannerType);
if (bannerEntry == nullptr)
{
log_error("Invalid banner object type. bannerType = ", _bannerType);
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE);
}
auto banner = GetBanner(_bannerIndex);
if (!banner->IsNull())
{
log_error("Banner index in use, bannerIndex = %u", _bannerIndex);
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE);
}
TileElement* newTileElement = tile_element_insert({ _loc, _loc.z + (2 * COORDS_Z_STEP) }, 0b0000);
assert(newTileElement != nullptr);
banner->flags = 0;
banner->text = {};
banner->text_colour = 2;
banner->type = _bannerType; // Banner must be deleted after this point in an early return
banner->colour = _primaryColour;
banner->position = TileCoordsXY(_loc);
newTileElement->SetType(TILE_ELEMENT_TYPE_BANNER);
BannerElement* bannerElement = newTileElement->AsBanner();
bannerElement->SetClearanceZ(_loc.z + PATH_CLEARANCE);
bannerElement->SetPosition(_loc.direction);
bannerElement->ResetAllowedEdges();
bannerElement->SetIndex(_bannerIndex);
if (GetFlags() & GAME_COMMAND_FLAG_GHOST)
{
bannerElement->SetGhost(true);
}
map_invalidate_tile_full(_loc);
map_animation_create(MAP_ANIMATION_TYPE_BANNER, CoordsXYZ{ _loc, bannerElement->GetBaseZ() });
res->Cost = bannerEntry->banner.price;
return res;
}
private:
PathElement* GetValidPathElement() const
{
TileElement* tileElement = map_get_first_element_at(_loc);
do
{
if (tileElement == nullptr)
break;
if (tileElement->GetType() != TILE_ELEMENT_TYPE_PATH)
continue;
auto pathElement = tileElement->AsPath();
if (pathElement->GetBaseZ() != _loc.z && pathElement->GetBaseZ() != _loc.z - PATH_HEIGHT_STEP)
continue;
if (!(pathElement->GetEdges() & (1 << _loc.direction)))
continue;
if (pathElement->IsGhost() && !(GetFlags() & GAME_COMMAND_FLAG_GHOST))
continue;
return pathElement;
} while (!(tileElement++)->IsLastForTile());
return nullptr;
}
};

View File

@ -0,0 +1,137 @@
/*****************************************************************************
* 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.
*****************************************************************************/
#include "BannerRemoveAction.h"
#include "../management/Finance.h"
#include "../world/Banner.h"
#include "../world/MapAnimation.h"
#include "../world/Scenery.h"
#include "GameAction.h"
void BannerRemoveAction::AcceptParameters(GameActionParameterVisitor& visitor)
{
visitor.Visit(_loc);
}
void BannerRemoveAction::Serialise(DataSerialiser& stream)
{
GameAction::Serialise(stream);
stream << DS_TAG(_loc);
}
GameActions::Result::Ptr BannerRemoveAction::Query() const
{
auto res = MakeResult();
res->Expenditure = ExpenditureType::Landscaping;
res->Position.x = _loc.x + 16;
res->Position.y = _loc.y + 16;
res->Position.z = _loc.z;
res->ErrorTitle = STR_CANT_REMOVE_THIS;
if (!LocationValid(_loc) || !map_can_build_at({ _loc.x, _loc.y, _loc.z - 16 }))
{
return MakeResult(GameActions::Status::NotOwned, STR_CANT_REMOVE_THIS, STR_LAND_NOT_OWNED_BY_PARK);
}
BannerElement* bannerElement = GetBannerElementAt();
if (bannerElement == nullptr)
{
log_error("Invalid banner location, x = %d, y = %d, z = %d, direction = %d", _loc.x, _loc.y, _loc.z, _loc.direction);
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REMOVE_THIS);
}
if (bannerElement->GetIndex() >= MAX_BANNERS || bannerElement->GetIndex() == BANNER_INDEX_NULL)
{
log_error("Invalid banner index. index = ", bannerElement->GetIndex());
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REMOVE_THIS);
}
auto banner = bannerElement->GetBanner();
if (banner == nullptr)
{
log_error("Invalid banner index. index = ", bannerElement->GetIndex());
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REMOVE_THIS);
}
rct_scenery_entry* bannerEntry = get_banner_entry(banner->type);
if (bannerEntry != nullptr)
{
res->Cost = -((bannerEntry->banner.price * 3) / 4);
}
return res;
}
GameActions::Result::Ptr BannerRemoveAction::Execute() const
{
auto res = MakeResult();
res->Expenditure = ExpenditureType::Landscaping;
res->Position.x = _loc.x + 16;
res->Position.y = _loc.y + 16;
res->Position.z = _loc.z;
res->ErrorTitle = STR_CANT_REMOVE_THIS;
BannerElement* bannerElement = GetBannerElementAt();
if (bannerElement == nullptr)
{
log_error("Invalid banner location, x = %d, y = %d, z = %d, direction = %d", _loc.x, _loc.y, _loc.z, _loc.direction);
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REMOVE_THIS);
}
if (bannerElement->GetIndex() >= MAX_BANNERS || bannerElement->GetIndex() == BANNER_INDEX_NULL)
{
log_error("Invalid banner index. index = ", bannerElement->GetIndex());
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REMOVE_THIS);
}
auto banner = bannerElement->GetBanner();
if (banner == nullptr)
{
log_error("Invalid banner index. index = ", bannerElement->GetIndex());
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REMOVE_THIS);
}
rct_scenery_entry* bannerEntry = get_banner_entry(banner->type);
if (bannerEntry != nullptr)
{
res->Cost = -((bannerEntry->banner.price * 3) / 4);
}
tile_element_remove_banner_entry(reinterpret_cast<TileElement*>(bannerElement));
map_invalidate_tile_zoom1({ _loc, _loc.z, _loc.z + 32 });
bannerElement->Remove();
return res;
}
BannerElement* BannerRemoveAction::GetBannerElementAt() const
{
TileElement* tileElement = map_get_first_element_at(_loc);
// Find the banner element at known z and position
do
{
if (tileElement == nullptr)
break;
if (tileElement->GetType() != TILE_ELEMENT_TYPE_BANNER)
continue;
if (tileElement->GetBaseZ() != _loc.z)
continue;
if (tileElement->IsGhost() && !(GetFlags() & GAME_COMMAND_FLAG_GHOST))
continue;
if (tileElement->AsBanner()->GetPosition() != _loc.direction)
continue;
return tileElement->AsBanner();
} while (!(tileElement++)->IsLastForTile());
return nullptr;
}

View File

@ -0,0 +1,39 @@
/*****************************************************************************
* 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
#include "GameAction.h"
DEFINE_GAME_ACTION(BannerRemoveAction, GAME_COMMAND_REMOVE_BANNER, GameActions::Result)
{
private:
CoordsXYZD _loc;
public:
BannerRemoveAction() = default;
BannerRemoveAction(const CoordsXYZD& loc)
: _loc(loc)
{
}
void AcceptParameters(GameActionParameterVisitor & visitor) override;
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags();
}
void Serialise(DataSerialiser & stream) override;
GameActions::Result::Ptr Query() const override;
GameActions::Result::Ptr Execute() const override;
private:
BannerElement* GetBannerElementAt() const;
};

View File

@ -1,158 +0,0 @@
/*****************************************************************************
* 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
#include "../management/Finance.h"
#include "../world/Banner.h"
#include "../world/MapAnimation.h"
#include "../world/Scenery.h"
#include "GameAction.h"
DEFINE_GAME_ACTION(BannerRemoveAction, GAME_COMMAND_REMOVE_BANNER, GameActions::Result)
{
private:
CoordsXYZD _loc;
public:
BannerRemoveAction() = default;
BannerRemoveAction(const CoordsXYZD& loc)
: _loc(loc)
{
}
void AcceptParameters(GameActionParameterVisitor & visitor) override
{
visitor.Visit(_loc);
}
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags();
}
void Serialise(DataSerialiser & stream) override
{
GameAction::Serialise(stream);
stream << DS_TAG(_loc);
}
GameActions::Result::Ptr Query() const override
{
auto res = MakeResult();
res->Expenditure = ExpenditureType::Landscaping;
res->Position.x = _loc.x + 16;
res->Position.y = _loc.y + 16;
res->Position.z = _loc.z;
res->ErrorTitle = STR_CANT_REMOVE_THIS;
if (!LocationValid(_loc) || !map_can_build_at({ _loc.x, _loc.y, _loc.z - 16 }))
{
return MakeResult(GameActions::Status::NotOwned, STR_CANT_REMOVE_THIS, STR_LAND_NOT_OWNED_BY_PARK);
}
BannerElement* bannerElement = GetBannerElementAt();
if (bannerElement == nullptr)
{
log_error(
"Invalid banner location, x = %d, y = %d, z = %d, direction = %d", _loc.x, _loc.y, _loc.z, _loc.direction);
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REMOVE_THIS);
}
if (bannerElement->GetIndex() >= MAX_BANNERS || bannerElement->GetIndex() == BANNER_INDEX_NULL)
{
log_error("Invalid banner index. index = ", bannerElement->GetIndex());
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REMOVE_THIS);
}
auto banner = bannerElement->GetBanner();
if (banner == nullptr)
{
log_error("Invalid banner index. index = ", bannerElement->GetIndex());
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REMOVE_THIS);
}
rct_scenery_entry* bannerEntry = get_banner_entry(banner->type);
if (bannerEntry != nullptr)
{
res->Cost = -((bannerEntry->banner.price * 3) / 4);
}
return res;
}
GameActions::Result::Ptr Execute() const override
{
auto res = MakeResult();
res->Expenditure = ExpenditureType::Landscaping;
res->Position.x = _loc.x + 16;
res->Position.y = _loc.y + 16;
res->Position.z = _loc.z;
res->ErrorTitle = STR_CANT_REMOVE_THIS;
BannerElement* bannerElement = GetBannerElementAt();
if (bannerElement == nullptr)
{
log_error(
"Invalid banner location, x = %d, y = %d, z = %d, direction = %d", _loc.x, _loc.y, _loc.z, _loc.direction);
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REMOVE_THIS);
}
if (bannerElement->GetIndex() >= MAX_BANNERS || bannerElement->GetIndex() == BANNER_INDEX_NULL)
{
log_error("Invalid banner index. index = ", bannerElement->GetIndex());
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REMOVE_THIS);
}
auto banner = bannerElement->GetBanner();
if (banner == nullptr)
{
log_error("Invalid banner index. index = ", bannerElement->GetIndex());
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REMOVE_THIS);
}
rct_scenery_entry* bannerEntry = get_banner_entry(banner->type);
if (bannerEntry != nullptr)
{
res->Cost = -((bannerEntry->banner.price * 3) / 4);
}
tile_element_remove_banner_entry(reinterpret_cast<TileElement*>(bannerElement));
map_invalidate_tile_zoom1({ _loc, _loc.z, _loc.z + 32 });
bannerElement->Remove();
return res;
}
private:
BannerElement* GetBannerElementAt() const
{
TileElement* tileElement = map_get_first_element_at(_loc);
// Find the banner element at known z and position
do
{
if (tileElement == nullptr)
break;
if (tileElement->GetType() != TILE_ELEMENT_TYPE_BANNER)
continue;
if (tileElement->GetBaseZ() != _loc.z)
continue;
if (tileElement->IsGhost() && !(GetFlags() & GAME_COMMAND_FLAG_GHOST))
continue;
if (tileElement->AsBanner()->GetPosition() != _loc.direction)
continue;
return tileElement->AsBanner();
} while (!(tileElement++)->IsLastForTile());
return nullptr;
}
};

View File

@ -0,0 +1,94 @@
/*****************************************************************************
* 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.
*****************************************************************************/
#include "BannerSetColourAction.h"
#include "../Context.h"
#include "../management/Finance.h"
#include "../windows/Intent.h"
#include "../world/Banner.h"
#include "GameAction.h"
void BannerSetColourAction::AcceptParameters(GameActionParameterVisitor& visitor)
{
visitor.Visit(_loc);
visitor.Visit("primaryColour", _primaryColour);
}
void BannerSetColourAction::Serialise(DataSerialiser& stream)
{
GameAction::Serialise(stream);
stream << DS_TAG(_loc) << DS_TAG(_primaryColour);
}
GameActions::Result::Ptr BannerSetColourAction::Query() const
{
return QueryExecute(false);
}
GameActions::Result::Ptr BannerSetColourAction::Execute() const
{
return QueryExecute(true);
}
GameActions::Result::Ptr BannerSetColourAction::QueryExecute(bool isExecuting) const
{
auto res = MakeResult();
res->Expenditure = ExpenditureType::Landscaping;
res->Position.x = _loc.x + 16;
res->Position.y = _loc.y + 16;
res->Position.z = _loc.z;
res->ErrorTitle = STR_CANT_REPAINT_THIS;
if (!LocationValid(_loc))
{
log_error("Invalid x / y coordinates: x = %d, y = %d", _loc.x, _loc.y);
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REPAINT_THIS);
}
if (_primaryColour > 31)
{
log_error("Invalid primary colour: colour = %u", _primaryColour);
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REPAINT_THIS);
}
if (!map_can_build_at({ _loc.x, _loc.y, _loc.z - 16 }))
{
return MakeResult(GameActions::Status::NotOwned, STR_CANT_REPAINT_THIS, STR_LAND_NOT_OWNED_BY_PARK);
}
auto bannerElement = map_get_banner_element_at(_loc, _loc.direction);
if (bannerElement == nullptr)
{
log_error("Could not find banner at: x = %d, y = %d, z = %d, direction = %u", _loc.x, _loc.y, _loc.z, _loc.direction);
return MakeResult(GameActions::Status::Unknown, STR_CANT_REPAINT_THIS);
}
auto index = bannerElement->GetIndex();
if (index >= MAX_BANNERS || index == BANNER_INDEX_NULL)
{
log_error("Invalid banner index: index = %u", index);
return MakeResult(GameActions::Status::Unknown, STR_CANT_REPAINT_THIS);
}
if (isExecuting)
{
auto intent = Intent(INTENT_ACTION_UPDATE_BANNER);
intent.putExtra(INTENT_EXTRA_BANNER_INDEX, index);
context_broadcast_intent(&intent);
auto banner = GetBanner(index);
banner->colour = _primaryColour;
map_invalidate_tile_zoom1({ _loc, _loc.z, _loc.z + 32 });
}
return res;
}

View File

@ -0,0 +1,42 @@
/*****************************************************************************
* 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
#include "GameAction.h"
DEFINE_GAME_ACTION(BannerSetColourAction, GAME_COMMAND_SET_BANNER_COLOUR, GameActions::Result)
{
private:
CoordsXYZD _loc;
uint8_t _primaryColour{};
public:
BannerSetColourAction() = default;
BannerSetColourAction(const CoordsXYZD& loc, uint8_t primaryColour)
: _loc(loc)
, _primaryColour(primaryColour)
{
}
void AcceptParameters(GameActionParameterVisitor & visitor) override;
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags() | GameActions::Flags::AllowWhilePaused;
}
void Serialise(DataSerialiser & stream) override;
GameActions::Result::Ptr Query() const override;
GameActions::Result::Ptr Execute() const override;
private:
GameActions::Result::Ptr QueryExecute(bool isExecuting) const;
};

View File

@ -1,117 +0,0 @@
/*****************************************************************************
* 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
#include "../Context.h"
#include "../management/Finance.h"
#include "../windows/Intent.h"
#include "../world/Banner.h"
#include "GameAction.h"
DEFINE_GAME_ACTION(BannerSetColourAction, GAME_COMMAND_SET_BANNER_COLOUR, GameActions::Result)
{
private:
CoordsXYZD _loc;
uint8_t _primaryColour{};
public:
BannerSetColourAction() = default;
BannerSetColourAction(const CoordsXYZD& loc, uint8_t primaryColour)
: _loc(loc)
, _primaryColour(primaryColour)
{
}
void AcceptParameters(GameActionParameterVisitor & visitor) override
{
visitor.Visit(_loc);
visitor.Visit("primaryColour", _primaryColour);
}
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags() | GameActions::Flags::AllowWhilePaused;
}
void Serialise(DataSerialiser & stream) override
{
GameAction::Serialise(stream);
stream << DS_TAG(_loc) << DS_TAG(_primaryColour);
}
GameActions::Result::Ptr Query() const override
{
return QueryExecute(false);
}
GameActions::Result::Ptr Execute() const override
{
return QueryExecute(true);
}
private:
GameActions::Result::Ptr QueryExecute(bool isExecuting) const
{
auto res = MakeResult();
res->Expenditure = ExpenditureType::Landscaping;
res->Position.x = _loc.x + 16;
res->Position.y = _loc.y + 16;
res->Position.z = _loc.z;
res->ErrorTitle = STR_CANT_REPAINT_THIS;
if (!LocationValid(_loc))
{
log_error("Invalid x / y coordinates: x = %d, y = %d", _loc.x, _loc.y);
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REPAINT_THIS);
}
if (_primaryColour > 31)
{
log_error("Invalid primary colour: colour = %u", _primaryColour);
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REPAINT_THIS);
}
if (!map_can_build_at({ _loc.x, _loc.y, _loc.z - 16 }))
{
return MakeResult(GameActions::Status::NotOwned, STR_CANT_REPAINT_THIS, STR_LAND_NOT_OWNED_BY_PARK);
}
auto bannerElement = map_get_banner_element_at(_loc, _loc.direction);
if (bannerElement == nullptr)
{
log_error(
"Could not find banner at: x = %d, y = %d, z = %d, direction = %u", _loc.x, _loc.y, _loc.z, _loc.direction);
return MakeResult(GameActions::Status::Unknown, STR_CANT_REPAINT_THIS);
}
auto index = bannerElement->GetIndex();
if (index >= MAX_BANNERS || index == BANNER_INDEX_NULL)
{
log_error("Invalid banner index: index = %u", index);
return MakeResult(GameActions::Status::Unknown, STR_CANT_REPAINT_THIS);
}
if (isExecuting)
{
auto intent = Intent(INTENT_ACTION_UPDATE_BANNER);
intent.putExtra(INTENT_EXTRA_BANNER_INDEX, index);
context_broadcast_intent(&intent);
auto banner = GetBanner(index);
banner->colour = _primaryColour;
map_invalidate_tile_zoom1({ _loc, _loc.z, _loc.z + 32 });
}
return res;
}
};

View File

@ -0,0 +1,58 @@
/*****************************************************************************
* 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.
*****************************************************************************/
#include "BannerSetNameAction.h"
#include "../Context.h"
#include "../core/String.hpp"
#include "../drawing/Drawing.h"
#include "../localisation/Localisation.h"
#include "../localisation/StringIds.h"
#include "../ui/UiContext.h"
#include "../windows/Intent.h"
#include "../world/Banner.h"
#include "../world/Sprite.h"
#include "GameAction.h"
void BannerSetNameAction::AcceptParameters(GameActionParameterVisitor& visitor)
{
visitor.Visit("id", _bannerIndex);
visitor.Visit("name", _name);
}
void BannerSetNameAction::Serialise(DataSerialiser& stream)
{
GameAction::Serialise(stream);
stream << DS_TAG(_bannerIndex) << DS_TAG(_name);
}
GameActions::Result::Ptr BannerSetNameAction::Query() const
{
if (_bannerIndex >= MAX_BANNERS)
{
log_warning("Invalid game command for setting banner name, banner id = %d", _bannerIndex);
return MakeResult(GameActions::Status::InvalidParameters, STR_NONE);
}
return MakeResult();
}
GameActions::Result::Ptr BannerSetNameAction::Execute() const
{
auto banner = GetBanner(_bannerIndex);
banner->text = _name;
auto intent = Intent(INTENT_ACTION_UPDATE_BANNER);
intent.putExtra(INTENT_EXTRA_BANNER_INDEX, _bannerIndex);
context_broadcast_intent(&intent);
scrolling_text_invalidate();
gfx_invalidate_screen();
return MakeResult();
}

View File

@ -0,0 +1,38 @@
/*****************************************************************************
* 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
#include "GameAction.h"
DEFINE_GAME_ACTION(BannerSetNameAction, GAME_COMMAND_SET_BANNER_NAME, GameActions::Result)
{
private:
BannerIndex _bannerIndex{ BANNER_INDEX_NULL };
std::string _name;
public:
BannerSetNameAction() = default;
BannerSetNameAction(BannerIndex bannerIndex, const std::string& name)
: _bannerIndex(bannerIndex)
, _name(name)
{
}
void AcceptParameters(GameActionParameterVisitor & visitor) override;
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags() | GameActions::Flags::AllowWhilePaused;
}
void Serialise(DataSerialiser & stream) override;
GameActions::Result::Ptr Query() const override;
GameActions::Result::Ptr Execute() const override;
};

View File

@ -1,78 +0,0 @@
/*****************************************************************************
* 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
#include "../Context.h"
#include "../core/String.hpp"
#include "../drawing/Drawing.h"
#include "../localisation/Localisation.h"
#include "../localisation/StringIds.h"
#include "../ui/UiContext.h"
#include "../windows/Intent.h"
#include "../world/Banner.h"
#include "../world/Sprite.h"
#include "GameAction.h"
DEFINE_GAME_ACTION(BannerSetNameAction, GAME_COMMAND_SET_BANNER_NAME, GameActions::Result)
{
private:
BannerIndex _bannerIndex{ BANNER_INDEX_NULL };
std::string _name;
public:
BannerSetNameAction() = default;
BannerSetNameAction(BannerIndex bannerIndex, const std::string& name)
: _bannerIndex(bannerIndex)
, _name(name)
{
}
void AcceptParameters(GameActionParameterVisitor & visitor) override
{
visitor.Visit("id", _bannerIndex);
visitor.Visit("name", _name);
}
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags() | GameActions::Flags::AllowWhilePaused;
}
void Serialise(DataSerialiser & stream) override
{
GameAction::Serialise(stream);
stream << DS_TAG(_bannerIndex) << DS_TAG(_name);
}
GameActions::Result::Ptr Query() const override
{
if (_bannerIndex >= MAX_BANNERS)
{
log_warning("Invalid game command for setting banner name, banner id = %d", _bannerIndex);
return MakeResult(GameActions::Status::InvalidParameters, STR_NONE);
}
return MakeResult();
}
GameActions::Result::Ptr Execute() const override
{
auto banner = GetBanner(_bannerIndex);
banner->text = _name;
auto intent = Intent(INTENT_ACTION_UPDATE_BANNER);
intent.putExtra(INTENT_EXTRA_BANNER_INDEX, _bannerIndex);
context_broadcast_intent(&intent);
scrolling_text_invalidate();
gfx_invalidate_screen();
return MakeResult();
}
};

View File

@ -0,0 +1,142 @@
/*****************************************************************************
* 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.
*****************************************************************************/
#include "BannerSetStyleAction.h"
#include "../Context.h"
#include "../management/Finance.h"
#include "../util/Util.h"
#include "../windows/Intent.h"
#include "../world/Banner.h"
#include "GameAction.h"
void BannerSetStyleAction::AcceptParameters(GameActionParameterVisitor& visitor)
{
visitor.Visit("id", _bannerIndex);
visitor.Visit("type", _type);
visitor.Visit("parameter", _parameter);
}
void BannerSetStyleAction::Serialise(DataSerialiser& stream)
{
GameAction::Serialise(stream);
stream << DS_TAG(_type) << DS_TAG(_bannerIndex) << DS_TAG(_parameter);
}
GameActions::Result::Ptr BannerSetStyleAction::Query() const
{
auto res = MakeResult();
if (_bannerIndex >= MAX_BANNERS || _bannerIndex == BANNER_INDEX_NULL)
{
return MakeResult(GameActions::Status::InvalidParameters, STR_INVALID_SELECTION_OF_OBJECTS);
}
auto banner = GetBanner(_bannerIndex);
res->Expenditure = ExpenditureType::Landscaping;
auto location = banner->position.ToCoordsXY().ToTileCentre();
res->Position = { location, tile_element_height(location) };
TileElement* tileElement = banner_get_tile_element(_bannerIndex);
if (tileElement == nullptr)
{
log_error("Could not find banner index = %u", _bannerIndex);
return MakeResult(GameActions::Status::InvalidParameters, STR_NONE);
}
switch (_type)
{
case BannerSetStyleType::PrimaryColour:
if (_parameter > 31)
{
log_error("Invalid primary colour: colour = %u", _parameter);
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REPAINT_THIS);
}
break;
case BannerSetStyleType::TextColour:
if (_parameter > 13)
{
log_error("Invalid text colour: colour = %u", _parameter);
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REPAINT_THIS);
}
break;
case BannerSetStyleType::NoEntry:
if (tileElement->AsBanner() == nullptr)
{
log_error("Tile element was not a banner.");
return MakeResult(GameActions::Status::Unknown, STR_NONE);
}
break;
default:
log_error("Invalid type: %u", _type);
return MakeResult(GameActions::Status::InvalidParameters, STR_NONE);
}
return res;
}
GameActions::Result::Ptr BannerSetStyleAction::Execute() const
{
auto res = MakeResult();
auto banner = GetBanner(_bannerIndex);
res->Expenditure = ExpenditureType::Landscaping;
auto location = banner->position.ToCoordsXY().ToTileCentre();
res->Position = { location, tile_element_height(location) };
TileElement* tileElement = banner_get_tile_element(_bannerIndex);
if (tileElement == nullptr)
{
log_error("Could not find banner index = %u", _bannerIndex);
return MakeResult(GameActions::Status::InvalidParameters, STR_NONE);
}
switch (_type)
{
case BannerSetStyleType::PrimaryColour:
banner->colour = _parameter;
break;
case BannerSetStyleType::TextColour:
banner->text_colour = _parameter;
break;
case BannerSetStyleType::NoEntry:
{
BannerElement* bannerElement = tileElement->AsBanner();
if (bannerElement == nullptr)
{
log_error("Tile element was not a banner.");
return MakeResult(GameActions::Status::Unknown, STR_NONE);
}
banner->flags &= ~BANNER_FLAG_NO_ENTRY;
banner->flags |= (_parameter != 0) ? BANNER_FLAG_NO_ENTRY : 0;
uint8_t allowedEdges = 0xF;
if (banner->flags & BANNER_FLAG_NO_ENTRY)
{
allowedEdges &= ~(1 << bannerElement->GetPosition());
}
bannerElement->SetAllowedEdges(allowedEdges);
break;
}
default:
log_error("Invalid type: %u", _type);
return MakeResult(GameActions::Status::InvalidParameters, STR_NONE);
}
auto intent = Intent(INTENT_ACTION_UPDATE_BANNER);
intent.putExtra(INTENT_EXTRA_BANNER_INDEX, _bannerIndex);
context_broadcast_intent(&intent);
return res;
}

View File

@ -0,0 +1,51 @@
/*****************************************************************************
* 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
#include "GameAction.h"
// There is also the BannerSetColourAction that sets primary colour but this action takes banner index rather than x, y, z,
// direction
enum class BannerSetStyleType : uint8_t
{
PrimaryColour,
TextColour,
NoEntry,
Count
};
DEFINE_GAME_ACTION(BannerSetStyleAction, GAME_COMMAND_SET_BANNER_STYLE, GameActions::Result)
{
private:
BannerSetStyleType _type{ BannerSetStyleType::Count };
BannerIndex _bannerIndex{ BANNER_INDEX_NULL };
uint8_t _parameter{};
public:
BannerSetStyleAction() = default;
BannerSetStyleAction(BannerSetStyleType type, uint8_t bannerIndex, uint8_t parameter)
: _type(type)
, _bannerIndex(bannerIndex)
, _parameter(parameter)
{
}
void AcceptParameters(GameActionParameterVisitor & visitor) override;
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags() | GameActions::Flags::AllowWhilePaused;
}
void Serialise(DataSerialiser & stream) override;
GameActions::Result::Ptr Query() const override;
GameActions::Result::Ptr Execute() const override;
};

View File

@ -1,175 +0,0 @@
/*****************************************************************************
* 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
#include "../Context.h"
#include "../management/Finance.h"
#include "../util/Util.h"
#include "../windows/Intent.h"
#include "../world/Banner.h"
#include "GameAction.h"
// There is also the BannerSetColourAction that sets primary colour but this action takes banner index rather than x, y, z,
// direction
enum class BannerSetStyleType : uint8_t
{
PrimaryColour,
TextColour,
NoEntry,
Count
};
DEFINE_GAME_ACTION(BannerSetStyleAction, GAME_COMMAND_SET_BANNER_STYLE, GameActions::Result)
{
private:
BannerSetStyleType _type{ BannerSetStyleType::Count };
BannerIndex _bannerIndex{ BANNER_INDEX_NULL };
uint8_t _parameter{};
public:
BannerSetStyleAction() = default;
BannerSetStyleAction(BannerSetStyleType type, uint8_t bannerIndex, uint8_t parameter)
: _type(type)
, _bannerIndex(bannerIndex)
, _parameter(parameter)
{
}
void AcceptParameters(GameActionParameterVisitor & visitor) override
{
visitor.Visit("id", _bannerIndex);
visitor.Visit("type", _type);
visitor.Visit("parameter", _parameter);
}
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags() | GameActions::Flags::AllowWhilePaused;
}
void Serialise(DataSerialiser & stream) override
{
GameAction::Serialise(stream);
stream << DS_TAG(_type) << DS_TAG(_bannerIndex) << DS_TAG(_parameter);
}
GameActions::Result::Ptr Query() const override
{
auto res = MakeResult();
if (_bannerIndex >= MAX_BANNERS || _bannerIndex == BANNER_INDEX_NULL)
{
return MakeResult(GameActions::Status::InvalidParameters, STR_INVALID_SELECTION_OF_OBJECTS);
}
auto banner = GetBanner(_bannerIndex);
res->Expenditure = ExpenditureType::Landscaping;
auto location = banner->position.ToCoordsXY().ToTileCentre();
res->Position = { location, tile_element_height(location) };
TileElement* tileElement = banner_get_tile_element(_bannerIndex);
if (tileElement == nullptr)
{
log_error("Could not find banner index = %u", _bannerIndex);
return MakeResult(GameActions::Status::InvalidParameters, STR_NONE);
}
switch (_type)
{
case BannerSetStyleType::PrimaryColour:
if (_parameter > 31)
{
log_error("Invalid primary colour: colour = %u", _parameter);
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REPAINT_THIS);
}
break;
case BannerSetStyleType::TextColour:
if (_parameter > 13)
{
log_error("Invalid text colour: colour = %u", _parameter);
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REPAINT_THIS);
}
break;
case BannerSetStyleType::NoEntry:
if (tileElement->AsBanner() == nullptr)
{
log_error("Tile element was not a banner.");
return MakeResult(GameActions::Status::Unknown, STR_NONE);
}
break;
default:
log_error("Invalid type: %u", _type);
return MakeResult(GameActions::Status::InvalidParameters, STR_NONE);
}
return res;
}
GameActions::Result::Ptr Execute() const override
{
auto res = MakeResult();
auto banner = GetBanner(_bannerIndex);
res->Expenditure = ExpenditureType::Landscaping;
auto location = banner->position.ToCoordsXY().ToTileCentre();
res->Position = { location, tile_element_height(location) };
TileElement* tileElement = banner_get_tile_element(_bannerIndex);
if (tileElement == nullptr)
{
log_error("Could not find banner index = %u", _bannerIndex);
return MakeResult(GameActions::Status::InvalidParameters, STR_NONE);
}
switch (_type)
{
case BannerSetStyleType::PrimaryColour:
banner->colour = _parameter;
break;
case BannerSetStyleType::TextColour:
banner->text_colour = _parameter;
break;
case BannerSetStyleType::NoEntry:
{
BannerElement* bannerElement = tileElement->AsBanner();
if (bannerElement == nullptr)
{
log_error("Tile element was not a banner.");
return MakeResult(GameActions::Status::Unknown, STR_NONE);
}
banner->flags &= ~BANNER_FLAG_NO_ENTRY;
banner->flags |= (_parameter != 0) ? BANNER_FLAG_NO_ENTRY : 0;
uint8_t allowedEdges = 0xF;
if (banner->flags & BANNER_FLAG_NO_ENTRY)
{
allowedEdges &= ~(1 << bannerElement->GetPosition());
}
bannerElement->SetAllowedEdges(allowedEdges);
break;
}
default:
log_error("Invalid type: %u", _type);
return MakeResult(GameActions::Status::InvalidParameters, STR_NONE);
}
auto intent = Intent(INTENT_ACTION_UPDATE_BANNER);
intent.putExtra(INTENT_EXTRA_BANNER_INDEX, _bannerIndex);
context_broadcast_intent(&intent);
return res;
}
};

View File

@ -0,0 +1,202 @@
/*****************************************************************************
* 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.
*****************************************************************************/
#include "ClearAction.h"
#include "../Context.h"
#include "../core/MemoryStream.h"
#include "../drawing/Drawing.h"
#include "../localisation/StringIds.h"
#include "../management/Finance.h"
#include "../world/LargeScenery.h"
#include "../world/Location.hpp"
#include "../world/Map.h"
#include "FootpathRemoveAction.h"
#include "LargeSceneryRemoveAction.h"
#include "SmallSceneryRemoveAction.h"
#include "WallRemoveAction.h"
#include <algorithm>
void ClearAction::Serialise(DataSerialiser& stream)
{
GameAction::Serialise(stream);
stream << DS_TAG(_range) << DS_TAG(_itemsToClear);
}
GameActions::Result::Ptr ClearAction::Query() const
{
return QueryExecute(false);
}
GameActions::Result::Ptr ClearAction::Execute() const
{
return QueryExecute(true);
}
GameActions::Result::Ptr ClearAction::CreateResult() const
{
auto result = MakeResult();
result->ErrorTitle = STR_UNABLE_TO_REMOVE_ALL_SCENERY_FROM_HERE;
result->Expenditure = ExpenditureType::Landscaping;
auto x = (_range.GetLeft() + _range.GetRight()) / 2 + 16;
auto y = (_range.GetTop() + _range.GetBottom()) / 2 + 16;
auto z = tile_element_height({ x, y });
result->Position = CoordsXYZ(x, y, z);
return result;
}
GameActions::Result::Ptr ClearAction::QueryExecute(bool executing) const
{
auto result = CreateResult();
auto noValidTiles = true;
auto error = GameActions::Status::Ok;
rct_string_id errorMessage = STR_NONE;
money32 totalCost = 0;
auto x0 = std::max(_range.GetLeft(), 32);
auto y0 = std::max(_range.GetTop(), 32);
auto x1 = std::min(_range.GetRight(), static_cast<int32_t>(gMapSizeMaxXY));
auto y1 = std::min(_range.GetBottom(), static_cast<int32_t>(gMapSizeMaxXY));
for (int32_t y = y0; y <= y1; y += COORDS_XY_STEP)
{
for (int32_t x = x0; x <= x1; x += COORDS_XY_STEP)
{
if (LocationValid({ x, y }) && MapCanClearAt({ x, y }))
{
auto cost = ClearSceneryFromTile({ x, y }, executing);
if (cost != MONEY32_UNDEFINED)
{
noValidTiles = false;
totalCost += cost;
}
}
else
{
error = GameActions::Status::NotOwned;
errorMessage = STR_LAND_NOT_OWNED_BY_PARK;
}
}
}
if (_itemsToClear & CLEARABLE_ITEMS::SCENERY_LARGE)
{
ResetClearLargeSceneryFlag();
}
if (noValidTiles)
{
result->Error = error;
result->ErrorMessage = errorMessage;
}
result->Cost = totalCost;
return result;
}
money32 ClearAction::ClearSceneryFromTile(const CoordsXY& tilePos, bool executing) const
{
// Pass down all flags.
TileElement* tileElement = nullptr;
money32 totalCost = 0;
bool tileEdited;
do
{
tileEdited = false;
tileElement = map_get_first_element_at(tilePos);
if (tileElement == nullptr)
return totalCost;
do
{
if (tileElement->IsGhost())
continue;
auto type = tileElement->GetType();
switch (type)
{
case TILE_ELEMENT_TYPE_PATH:
if (_itemsToClear & CLEARABLE_ITEMS::SCENERY_FOOTPATH)
{
auto footpathRemoveAction = FootpathRemoveAction({ tilePos, tileElement->GetBaseZ() });
footpathRemoveAction.SetFlags(GetFlags());
auto res = executing ? GameActions::ExecuteNested(&footpathRemoveAction)
: GameActions::QueryNested(&footpathRemoveAction);
if (res->Error == GameActions::Status::Ok)
{
totalCost += res->Cost;
tileEdited = executing;
}
}
break;
case TILE_ELEMENT_TYPE_SMALL_SCENERY:
if (_itemsToClear & CLEARABLE_ITEMS::SCENERY_SMALL)
{
auto removeSceneryAction = SmallSceneryRemoveAction(
{ tilePos, tileElement->GetBaseZ() }, tileElement->AsSmallScenery()->GetSceneryQuadrant(),
tileElement->AsSmallScenery()->GetEntryIndex());
removeSceneryAction.SetFlags(GetFlags());
auto res = executing ? GameActions::ExecuteNested(&removeSceneryAction)
: GameActions::QueryNested(&removeSceneryAction);
if (res->Error == GameActions::Status::Ok)
{
totalCost += res->Cost;
tileEdited = executing;
}
}
break;
case TILE_ELEMENT_TYPE_WALL:
if (_itemsToClear & CLEARABLE_ITEMS::SCENERY_SMALL)
{
CoordsXYZD wallLocation = { tilePos, tileElement->GetBaseZ(), tileElement->GetDirection() };
auto wallRemoveAction = WallRemoveAction(wallLocation);
wallRemoveAction.SetFlags(GetFlags());
auto res = executing ? GameActions::ExecuteNested(&wallRemoveAction)
: GameActions::QueryNested(&wallRemoveAction);
if (res->Error == GameActions::Status::Ok)
{
totalCost += res->Cost;
tileEdited = executing;
}
}
break;
case TILE_ELEMENT_TYPE_LARGE_SCENERY:
if (_itemsToClear & CLEARABLE_ITEMS::SCENERY_LARGE)
{
auto removeSceneryAction = LargeSceneryRemoveAction(
{ tilePos, tileElement->GetBaseZ(), tileElement->GetDirection() },
tileElement->AsLargeScenery()->GetSequenceIndex());
removeSceneryAction.SetFlags(GetFlags() | GAME_COMMAND_FLAG_PATH_SCENERY);
auto res = executing ? GameActions::ExecuteNested(&removeSceneryAction)
: GameActions::QueryNested(&removeSceneryAction);
if (res->Error == GameActions::Status::Ok)
{
totalCost += res->Cost;
tileEdited = executing;
}
}
break;
}
} while (!tileEdited && !(tileElement++)->IsLastForTile());
} while (tileEdited);
return totalCost;
}

View File

@ -0,0 +1,80 @@
/*****************************************************************************
* 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
#include "../OpenRCT2.h"
#include "../management/Finance.h"
#include "GameAction.h"
using namespace OpenRCT2;
using ClearableItems = uint8_t;
namespace CLEARABLE_ITEMS
{
constexpr ClearableItems SCENERY_SMALL = 1 << 0;
constexpr ClearableItems SCENERY_LARGE = 1 << 1;
constexpr ClearableItems SCENERY_FOOTPATH = 1 << 2;
} // namespace CLEARABLE_ITEMS
DEFINE_GAME_ACTION(ClearAction, GAME_COMMAND_CLEAR_SCENERY, GameActions::Result)
{
private:
MapRange _range;
ClearableItems _itemsToClear{};
public:
ClearAction() = default;
ClearAction(MapRange range, ClearableItems itemsToClear)
: _range(range)
, _itemsToClear(itemsToClear)
{
}
void Serialise(DataSerialiser & stream) override;
GameActions::Result::Ptr Query() const override;
GameActions::Result::Ptr Execute() const override;
private:
GameActions::Result::Ptr CreateResult() const;
GameActions::Result::Ptr QueryExecute(bool executing) const;
money32 ClearSceneryFromTile(const CoordsXY& tilePos, bool executing) const;
/**
* Function to clear the flag that is set to prevent cost duplication
* when using the clear scenery tool with large scenery.
*/
static void ResetClearLargeSceneryFlag()
{
// TODO: Improve efficiency of this
for (int32_t y = 0; y < MAXIMUM_MAP_SIZE_TECHNICAL; y++)
{
for (int32_t x = 0; x < MAXIMUM_MAP_SIZE_TECHNICAL; x++)
{
auto tileElement = map_get_first_element_at(TileCoordsXY{ x, y }.ToCoordsXY());
do
{
if (tileElement == nullptr)
break;
if (tileElement->GetType() == TILE_ELEMENT_TYPE_LARGE_SCENERY)
{
tileElement->AsLargeScenery()->SetIsAccounted(false);
}
} while (!(tileElement++)->IsLastForTile());
}
}
}
static bool MapCanClearAt(const CoordsXY& location)
{
return (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode
|| map_is_location_owned_or_has_rights(location);
}
};

View File

@ -1,261 +0,0 @@
/*****************************************************************************
* 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
#include "../Context.h"
#include "../core/MemoryStream.h"
#include "../drawing/Drawing.h"
#include "../localisation/StringIds.h"
#include "../management/Finance.h"
#include "../world/LargeScenery.h"
#include "../world/Location.hpp"
#include "../world/Map.h"
#include "FootpathRemoveAction.hpp"
#include "GameAction.h"
#include "LargeSceneryRemoveAction.hpp"
#include "SmallSceneryRemoveAction.hpp"
#include "WallRemoveAction.hpp"
#include <algorithm>
using namespace OpenRCT2;
using ClearableItems = uint8_t;
namespace CLEARABLE_ITEMS
{
constexpr ClearableItems SCENERY_SMALL = 1 << 0;
constexpr ClearableItems SCENERY_LARGE = 1 << 1;
constexpr ClearableItems SCENERY_FOOTPATH = 1 << 2;
} // namespace CLEARABLE_ITEMS
DEFINE_GAME_ACTION(ClearAction, GAME_COMMAND_CLEAR_SCENERY, GameActions::Result)
{
private:
MapRange _range;
ClearableItems _itemsToClear{};
public:
ClearAction() = default;
ClearAction(MapRange range, ClearableItems itemsToClear)
: _range(range)
, _itemsToClear(itemsToClear)
{
}
void Serialise(DataSerialiser & stream) override
{
GameAction::Serialise(stream);
stream << DS_TAG(_range) << DS_TAG(_itemsToClear);
}
GameActions::Result::Ptr Query() const override
{
return QueryExecute(false);
}
GameActions::Result::Ptr Execute() const override
{
return QueryExecute(true);
}
private:
GameActions::Result::Ptr CreateResult() const
{
auto result = MakeResult();
result->ErrorTitle = STR_UNABLE_TO_REMOVE_ALL_SCENERY_FROM_HERE;
result->Expenditure = ExpenditureType::Landscaping;
auto x = (_range.GetLeft() + _range.GetRight()) / 2 + 16;
auto y = (_range.GetTop() + _range.GetBottom()) / 2 + 16;
auto z = tile_element_height({ x, y });
result->Position = CoordsXYZ(x, y, z);
return result;
}
GameActions::Result::Ptr QueryExecute(bool executing) const
{
auto result = CreateResult();
auto noValidTiles = true;
auto error = GameActions::Status::Ok;
rct_string_id errorMessage = STR_NONE;
money32 totalCost = 0;
auto x0 = std::max(_range.GetLeft(), 32);
auto y0 = std::max(_range.GetTop(), 32);
auto x1 = std::min(_range.GetRight(), static_cast<int32_t>(gMapSizeMaxXY));
auto y1 = std::min(_range.GetBottom(), static_cast<int32_t>(gMapSizeMaxXY));
for (int32_t y = y0; y <= y1; y += COORDS_XY_STEP)
{
for (int32_t x = x0; x <= x1; x += COORDS_XY_STEP)
{
if (LocationValid({ x, y }) && MapCanClearAt({ x, y }))
{
auto cost = ClearSceneryFromTile({ x, y }, executing);
if (cost != MONEY32_UNDEFINED)
{
noValidTiles = false;
totalCost += cost;
}
}
else
{
error = GameActions::Status::NotOwned;
errorMessage = STR_LAND_NOT_OWNED_BY_PARK;
}
}
}
if (_itemsToClear & CLEARABLE_ITEMS::SCENERY_LARGE)
{
ResetClearLargeSceneryFlag();
}
if (noValidTiles)
{
result->Error = error;
result->ErrorMessage = errorMessage;
}
result->Cost = totalCost;
return result;
}
money32 ClearSceneryFromTile(const CoordsXY& tilePos, bool executing) const
{
// Pass down all flags.
TileElement* tileElement = nullptr;
money32 totalCost = 0;
bool tileEdited;
do
{
tileEdited = false;
tileElement = map_get_first_element_at(tilePos);
if (tileElement == nullptr)
return totalCost;
do
{
if (tileElement->IsGhost())
continue;
auto type = tileElement->GetType();
switch (type)
{
case TILE_ELEMENT_TYPE_PATH:
if (_itemsToClear & CLEARABLE_ITEMS::SCENERY_FOOTPATH)
{
auto footpathRemoveAction = FootpathRemoveAction({ tilePos, tileElement->GetBaseZ() });
footpathRemoveAction.SetFlags(GetFlags());
auto res = executing ? GameActions::ExecuteNested(&footpathRemoveAction)
: GameActions::QueryNested(&footpathRemoveAction);
if (res->Error == GameActions::Status::Ok)
{
totalCost += res->Cost;
tileEdited = executing;
}
}
break;
case TILE_ELEMENT_TYPE_SMALL_SCENERY:
if (_itemsToClear & CLEARABLE_ITEMS::SCENERY_SMALL)
{
auto removeSceneryAction = SmallSceneryRemoveAction(
{ tilePos, tileElement->GetBaseZ() }, tileElement->AsSmallScenery()->GetSceneryQuadrant(),
tileElement->AsSmallScenery()->GetEntryIndex());
removeSceneryAction.SetFlags(GetFlags());
auto res = executing ? GameActions::ExecuteNested(&removeSceneryAction)
: GameActions::QueryNested(&removeSceneryAction);
if (res->Error == GameActions::Status::Ok)
{
totalCost += res->Cost;
tileEdited = executing;
}
}
break;
case TILE_ELEMENT_TYPE_WALL:
if (_itemsToClear & CLEARABLE_ITEMS::SCENERY_SMALL)
{
CoordsXYZD wallLocation = { tilePos, tileElement->GetBaseZ(), tileElement->GetDirection() };
auto wallRemoveAction = WallRemoveAction(wallLocation);
wallRemoveAction.SetFlags(GetFlags());
auto res = executing ? GameActions::ExecuteNested(&wallRemoveAction)
: GameActions::QueryNested(&wallRemoveAction);
if (res->Error == GameActions::Status::Ok)
{
totalCost += res->Cost;
tileEdited = executing;
}
}
break;
case TILE_ELEMENT_TYPE_LARGE_SCENERY:
if (_itemsToClear & CLEARABLE_ITEMS::SCENERY_LARGE)
{
auto removeSceneryAction = LargeSceneryRemoveAction(
{ tilePos, tileElement->GetBaseZ(), tileElement->GetDirection() },
tileElement->AsLargeScenery()->GetSequenceIndex());
removeSceneryAction.SetFlags(GetFlags() | GAME_COMMAND_FLAG_PATH_SCENERY);
auto res = executing ? GameActions::ExecuteNested(&removeSceneryAction)
: GameActions::QueryNested(&removeSceneryAction);
if (res->Error == GameActions::Status::Ok)
{
totalCost += res->Cost;
tileEdited = executing;
}
}
break;
}
} while (!tileEdited && !(tileElement++)->IsLastForTile());
} while (tileEdited);
return totalCost;
}
/**
* Function to clear the flag that is set to prevent cost duplication
* when using the clear scenery tool with large scenery.
*/
static void ResetClearLargeSceneryFlag()
{
// TODO: Improve efficiency of this
for (int32_t y = 0; y < MAXIMUM_MAP_SIZE_TECHNICAL; y++)
{
for (int32_t x = 0; x < MAXIMUM_MAP_SIZE_TECHNICAL; x++)
{
auto tileElement = map_get_first_element_at(TileCoordsXY{ x, y }.ToCoordsXY());
do
{
if (tileElement == nullptr)
break;
if (tileElement->GetType() == TILE_ELEMENT_TYPE_LARGE_SCENERY)
{
tileElement->AsLargeScenery()->SetIsAccounted(false);
}
} while (!(tileElement++)->IsLastForTile());
}
}
}
static bool MapCanClearAt(const CoordsXY& location)
{
return (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode
|| map_is_location_owned_or_has_rights(location);
}
};

View File

@ -0,0 +1,41 @@
/*****************************************************************************
* 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.
*****************************************************************************/
#include "ClimateSetAction.h"
void ClimateSetAction::AcceptParameters(GameActionParameterVisitor& visitor)
{
visitor.Visit("climate", _climate);
}
void ClimateSetAction::Serialise(DataSerialiser& stream)
{
GameAction::Serialise(stream);
stream << DS_TAG(_climate);
}
GameActions::Result::Ptr ClimateSetAction::Query() const
{
if (_climate >= ClimateType::Count)
{
return std::make_unique<GameActions::Result>(GameActions::Status::InvalidParameters, STR_INVALID_CLIMATE_ID, STR_NONE);
}
return std::make_unique<GameActions::Result>();
}
GameActions::Result::Ptr ClimateSetAction::Execute() const
{
gClimate = ClimateType{ _climate };
gfx_invalidate_screen();
return std::make_unique<GameActions::Result>();
}

View File

@ -23,41 +23,14 @@ public:
: _climate(climate)
{
}
void AcceptParameters(GameActionParameterVisitor & visitor) override
{
visitor.Visit("climate", _climate);
}
void AcceptParameters(GameActionParameterVisitor & visitor) override;
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags() | GameActions::Flags::AllowWhilePaused;
return GameAction::GetActionFlags();
}
void Serialise(DataSerialiser & stream) override
{
GameAction::Serialise(stream);
stream << DS_TAG(_climate);
}
GameActions::Result::Ptr Query() const override
{
if (_climate >= ClimateType::Count)
{
return std::make_unique<GameActions::Result>(
GameActions::Status::InvalidParameters, STR_INVALID_CLIMATE_ID, STR_NONE);
}
return std::make_unique<GameActions::Result>();
}
GameActions::Result::Ptr Execute() const override
{
gClimate = ClimateType{ _climate };
gfx_invalidate_screen();
return std::make_unique<GameActions::Result>();
}
void Serialise(DataSerialiser & stream) override;
GameActions::Result::Ptr Query() const override;
GameActions::Result::Ptr Execute() const override;
};

View File

@ -0,0 +1,34 @@
/*****************************************************************************
* 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.
*****************************************************************************/
#ifdef ENABLE_SCRIPTING
# include "CustomAction.h"
# include "../Context.h"
# include "../scripting/ScriptEngine.h"
void CustomAction::Serialise(DataSerialiser& stream)
{
GameAction::Serialise(stream);
stream << DS_TAG(_id) << DS_TAG(_json);
}
GameActions::Result::Ptr CustomAction::Query() const
{
auto& scriptingEngine = OpenRCT2::GetContext()->GetScriptEngine();
return scriptingEngine.QueryOrExecuteCustomGameAction(_id, _json, false);
}
GameActions::Result::Ptr CustomAction::Execute() const
{
auto& scriptingEngine = OpenRCT2::GetContext()->GetScriptEngine();
return scriptingEngine.QueryOrExecuteCustomGameAction(_id, _json, true);
}
#endif

View File

@ -11,8 +11,6 @@
#ifdef ENABLE_SCRIPTING
# include "../Context.h"
# include "../scripting/ScriptEngine.h"
# include "GameAction.h"
DEFINE_GAME_ACTION(CustomAction, GAME_COMMAND_CUSTOM, GameActions::Result)
@ -44,23 +42,9 @@ public:
return GameAction::GetActionFlags() | GameActions::Flags::AllowWhilePaused;
}
void Serialise(DataSerialiser & stream) override
{
GameAction::Serialise(stream);
stream << DS_TAG(_id) << DS_TAG(_json);
}
GameActions::Result::Ptr Query() const override
{
auto& scriptingEngine = OpenRCT2::GetContext()->GetScriptEngine();
return scriptingEngine.QueryOrExecuteCustomGameAction(_id, _json, false);
}
GameActions::Result::Ptr Execute() const override
{
auto& scriptingEngine = OpenRCT2::GetContext()->GetScriptEngine();
return scriptingEngine.QueryOrExecuteCustomGameAction(_id, _json, true);
}
void Serialise(DataSerialiser & stream) override;
GameActions::Result::Ptr Query() const override;
GameActions::Result::Ptr Execute() const override;
};
#endif

View File

@ -0,0 +1,191 @@
/*****************************************************************************
* 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.
*****************************************************************************/
#include "FootpathAdditionPlaceAction.h"
#include "../Cheats.h"
#include "../OpenRCT2.h"
#include "../core/MemoryStream.h"
#include "../interface/Window.h"
#include "../localisation/StringIds.h"
#include "../management/Finance.h"
#include "../world/Footpath.h"
#include "../world/Location.hpp"
#include "../world/Park.h"
#include "../world/Scenery.h"
#include "../world/Wall.h"
void FootpathAdditionPlaceAction::AcceptParameters(GameActionParameterVisitor& visitor)
{
visitor.Visit(_loc);
visitor.Visit("object", _pathItemType);
}
void FootpathAdditionPlaceAction::Serialise(DataSerialiser& stream)
{
GameAction::Serialise(stream);
stream << DS_TAG(_loc) << DS_TAG(_pathItemType);
}
GameActions::Result::Ptr FootpathAdditionPlaceAction::Query() const
{
auto res = MakeResult();
res->Expenditure = ExpenditureType::Landscaping;
res->Position = _loc;
if (!LocationValid(_loc))
{
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE, STR_OFF_EDGE_OF_MAP);
}
if (!((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode) && !map_is_location_owned(_loc))
{
return MakeResult(GameActions::Status::Disallowed, STR_CANT_POSITION_THIS_HERE, STR_LAND_NOT_OWNED_BY_PARK);
}
if (_loc.z < FootpathMinHeight)
{
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE, STR_TOO_LOW);
}
if (_loc.z > FootpathMaxHeight)
{
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE, STR_TOO_HIGH);
}
auto tileElement = map_get_footpath_element(_loc);
if (tileElement == nullptr)
{
log_error("Could not find path element.");
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE);
}
auto pathElement = tileElement->AsPath();
if (pathElement->IsLevelCrossing(_loc))
{
return MakeResult(
GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE,
STR_CANNOT_BUILD_PATH_ADDITIONS_ON_LEVEL_CROSSINGS);
}
// No change
if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST) && pathElement->GetAddition() == _pathItemType && !(pathElement->IsBroken()))
{
return res;
}
if (_pathItemType != 0)
{
rct_scenery_entry* sceneryEntry = get_footpath_item_entry(_pathItemType - 1);
if (sceneryEntry == nullptr)
{
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE);
}
uint16_t sceneryFlags = sceneryEntry->path_bit.flags;
if ((sceneryFlags & PATH_BIT_FLAG_DONT_ALLOW_ON_SLOPE) && pathElement->IsSloped())
{
return MakeResult(
GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE, STR_CANT_BUILD_THIS_ON_SLOPED_FOOTPATH);
}
if ((sceneryFlags & PATH_BIT_FLAG_DONT_ALLOW_ON_QUEUE) && pathElement->IsQueue())
{
return MakeResult(
GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE, STR_CANNOT_PLACE_THESE_ON_QUEUE_LINE_AREA);
}
if (!(sceneryFlags & (PATH_BIT_FLAG_JUMPING_FOUNTAIN_WATER | PATH_BIT_FLAG_JUMPING_FOUNTAIN_SNOW))
&& (pathElement->GetEdges()) == 0x0F)
{
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE);
}
if ((sceneryFlags & PATH_BIT_FLAG_IS_QUEUE_SCREEN) && !pathElement->IsQueue())
{
return MakeResult(
GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE, STR_CAN_ONLY_PLACE_THESE_ON_QUEUE_AREA);
}
res->Cost = sceneryEntry->path_bit.price;
}
// Should place a ghost?
if (GetFlags() & GAME_COMMAND_FLAG_GHOST)
{
// Check if there is something on the path already
if (pathElement->HasAddition())
{
return MakeResult(GameActions::Status::ItemAlreadyPlaced, STR_CANT_POSITION_THIS_HERE);
}
}
return res;
}
GameActions::Result::Ptr FootpathAdditionPlaceAction::Execute() const
{
auto res = MakeResult();
res->Position = _loc;
res->Expenditure = ExpenditureType::Landscaping;
auto tileElement = map_get_footpath_element(_loc);
auto pathElement = tileElement->AsPath();
if (pathElement == nullptr)
{
log_error("Could not find path element.");
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE);
}
// No change
if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST) && pathElement->GetAddition() == _pathItemType && !(pathElement->IsBroken())
&& !pathElement->AdditionIsGhost())
{
return res;
}
if (_pathItemType != 0)
{
rct_scenery_entry* sceneryEntry = get_footpath_item_entry(_pathItemType - 1);
if (sceneryEntry == nullptr)
{
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE);
}
res->Cost = sceneryEntry->path_bit.price;
}
if (GetFlags() & GAME_COMMAND_FLAG_GHOST)
{
pathElement->SetAdditionIsGhost(true);
}
else
{
footpath_interrupt_peeps(_loc);
}
if ((_pathItemType != 0 && !(GetFlags() & GAME_COMMAND_FLAG_GHOST))
|| (_pathItemType == 0 && pathElement->AdditionIsGhost()))
{
pathElement->SetAdditionIsGhost(false);
}
pathElement->SetAddition(_pathItemType);
pathElement->SetIsBroken(false);
if (_pathItemType != 0)
{
rct_scenery_entry* scenery_entry = get_footpath_item_entry(_pathItemType - 1);
if (scenery_entry != nullptr && scenery_entry->path_bit.flags & PATH_BIT_FLAG_IS_BIN)
{
pathElement->SetAdditionStatus(255);
}
}
map_invalidate_tile_full(_loc);
return res;
}

View File

@ -0,0 +1,38 @@
/*****************************************************************************
* 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
#include "GameAction.h"
DEFINE_GAME_ACTION(FootpathAdditionPlaceAction, GAME_COMMAND_PLACE_FOOTPATH_ADDITION, GameActions::Result)
{
private:
CoordsXYZ _loc;
ObjectEntryIndex _pathItemType{};
public:
FootpathAdditionPlaceAction() = default;
FootpathAdditionPlaceAction(const CoordsXYZ& loc, ObjectEntryIndex pathItemType)
: _loc(loc)
, _pathItemType(pathItemType)
{
}
void AcceptParameters(GameActionParameterVisitor & visitor) override;
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags();
}
void Serialise(DataSerialiser & stream) override;
GameActions::Result::Ptr Query() const override;
GameActions::Result::Ptr Execute() const override;
};

View File

@ -1,216 +0,0 @@
/*****************************************************************************
* 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
#include "../Cheats.h"
#include "../OpenRCT2.h"
#include "../core/MemoryStream.h"
#include "../interface/Window.h"
#include "../localisation/StringIds.h"
#include "../management/Finance.h"
#include "../world/Footpath.h"
#include "../world/Location.hpp"
#include "../world/Park.h"
#include "../world/Scenery.h"
#include "../world/Wall.h"
#include "GameAction.h"
DEFINE_GAME_ACTION(FootpathAdditionPlaceAction, GAME_COMMAND_PLACE_FOOTPATH_ADDITION, GameActions::Result)
{
private:
CoordsXYZ _loc;
ObjectEntryIndex _pathItemType{};
public:
FootpathAdditionPlaceAction() = default;
FootpathAdditionPlaceAction(const CoordsXYZ& loc, ObjectEntryIndex pathItemType)
: _loc(loc)
, _pathItemType(pathItemType)
{
}
void AcceptParameters(GameActionParameterVisitor & visitor) override
{
visitor.Visit(_loc);
visitor.Visit("object", _pathItemType);
}
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags();
}
void Serialise(DataSerialiser & stream) override
{
GameAction::Serialise(stream);
stream << DS_TAG(_loc) << DS_TAG(_pathItemType);
}
GameActions::Result::Ptr Query() const override
{
auto res = MakeResult();
res->Expenditure = ExpenditureType::Landscaping;
res->Position = _loc;
if (!LocationValid(_loc))
{
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE, STR_OFF_EDGE_OF_MAP);
}
if (!((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode) && !map_is_location_owned(_loc))
{
return MakeResult(GameActions::Status::Disallowed, STR_CANT_POSITION_THIS_HERE, STR_LAND_NOT_OWNED_BY_PARK);
}
if (_loc.z < FootpathMinHeight)
{
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE, STR_TOO_LOW);
}
if (_loc.z > FootpathMaxHeight)
{
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE, STR_TOO_HIGH);
}
auto tileElement = map_get_footpath_element(_loc);
if (tileElement == nullptr)
{
log_error("Could not find path element.");
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE);
}
auto pathElement = tileElement->AsPath();
if (pathElement->IsLevelCrossing(_loc))
{
return MakeResult(
GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE,
STR_CANNOT_BUILD_PATH_ADDITIONS_ON_LEVEL_CROSSINGS);
}
// No change
if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST) && pathElement->GetAddition() == _pathItemType
&& !(pathElement->IsBroken()))
{
return res;
}
if (_pathItemType != 0)
{
rct_scenery_entry* sceneryEntry = get_footpath_item_entry(_pathItemType - 1);
if (sceneryEntry == nullptr)
{
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE);
}
uint16_t sceneryFlags = sceneryEntry->path_bit.flags;
if ((sceneryFlags & PATH_BIT_FLAG_DONT_ALLOW_ON_SLOPE) && pathElement->IsSloped())
{
return MakeResult(
GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE,
STR_CANT_BUILD_THIS_ON_SLOPED_FOOTPATH);
}
if ((sceneryFlags & PATH_BIT_FLAG_DONT_ALLOW_ON_QUEUE) && pathElement->IsQueue())
{
return MakeResult(
GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE,
STR_CANNOT_PLACE_THESE_ON_QUEUE_LINE_AREA);
}
if (!(sceneryFlags & (PATH_BIT_FLAG_JUMPING_FOUNTAIN_WATER | PATH_BIT_FLAG_JUMPING_FOUNTAIN_SNOW))
&& (pathElement->GetEdges()) == 0x0F)
{
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE);
}
if ((sceneryFlags & PATH_BIT_FLAG_IS_QUEUE_SCREEN) && !pathElement->IsQueue())
{
return MakeResult(
GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE,
STR_CAN_ONLY_PLACE_THESE_ON_QUEUE_AREA);
}
res->Cost = sceneryEntry->path_bit.price;
}
// Should place a ghost?
if (GetFlags() & GAME_COMMAND_FLAG_GHOST)
{
// Check if there is something on the path already
if (pathElement->HasAddition())
{
return MakeResult(GameActions::Status::ItemAlreadyPlaced, STR_CANT_POSITION_THIS_HERE);
}
}
return res;
}
GameActions::Result::Ptr Execute() const override
{
auto res = MakeResult();
res->Position = _loc;
res->Expenditure = ExpenditureType::Landscaping;
auto tileElement = map_get_footpath_element(_loc);
auto pathElement = tileElement->AsPath();
if (pathElement == nullptr)
{
log_error("Could not find path element.");
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE);
}
// No change
if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST) && pathElement->GetAddition() == _pathItemType && !(pathElement->IsBroken())
&& !pathElement->AdditionIsGhost())
{
return res;
}
if (_pathItemType != 0)
{
rct_scenery_entry* sceneryEntry = get_footpath_item_entry(_pathItemType - 1);
if (sceneryEntry == nullptr)
{
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_POSITION_THIS_HERE);
}
res->Cost = sceneryEntry->path_bit.price;
}
if (GetFlags() & GAME_COMMAND_FLAG_GHOST)
{
pathElement->SetAdditionIsGhost(true);
}
else
{
footpath_interrupt_peeps(_loc);
}
if ((_pathItemType != 0 && !(GetFlags() & GAME_COMMAND_FLAG_GHOST))
|| (_pathItemType == 0 && pathElement->AdditionIsGhost()))
{
pathElement->SetAdditionIsGhost(false);
}
pathElement->SetAddition(_pathItemType);
pathElement->SetIsBroken(false);
if (_pathItemType != 0)
{
rct_scenery_entry* scenery_entry = get_footpath_item_entry(_pathItemType - 1);
if (scenery_entry != nullptr && scenery_entry->path_bit.flags & PATH_BIT_FLAG_IS_BIN)
{
pathElement->SetAdditionStatus(255);
}
}
map_invalidate_tile_full(_loc);
return res;
}
};

View File

@ -0,0 +1,105 @@
/*****************************************************************************
* 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.
*****************************************************************************/
#include "FootpathAdditionRemoveAction.h"
#include "../Cheats.h"
#include "../OpenRCT2.h"
#include "../core/MemoryStream.h"
#include "../interface/Window.h"
#include "../localisation/StringIds.h"
#include "../management/Finance.h"
#include "../world/Footpath.h"
#include "../world/Location.hpp"
#include "../world/Park.h"
#include "../world/Wall.h"
void FootpathAdditionRemoveAction::AcceptParameters(GameActionParameterVisitor& visitor)
{
visitor.Visit(_loc);
}
void FootpathAdditionRemoveAction::Serialise(DataSerialiser& stream)
{
GameAction::Serialise(stream);
stream << DS_TAG(_loc);
}
GameActions::Result::Ptr FootpathAdditionRemoveAction::Query() const
{
if (!LocationValid(_loc))
{
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REMOVE_THIS, STR_OFF_EDGE_OF_MAP);
}
if (!((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode) && !map_is_location_owned(_loc))
{
return MakeResult(GameActions::Status::Disallowed, STR_CANT_REMOVE_THIS, STR_LAND_NOT_OWNED_BY_PARK);
}
if (_loc.z < FootpathMinHeight)
{
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REMOVE_THIS, STR_TOO_LOW);
}
if (_loc.z > FootpathMaxHeight)
{
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REMOVE_THIS, STR_TOO_HIGH);
}
auto tileElement = map_get_footpath_element(_loc);
if (tileElement == nullptr)
{
log_warning("Could not find path element.");
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REMOVE_THIS);
}
auto pathElement = tileElement->AsPath();
if (pathElement == nullptr)
{
log_warning("Could not find path element.");
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REMOVE_THIS);
}
if (!pathElement->AdditionIsGhost() && (GetFlags() & GAME_COMMAND_FLAG_GHOST))
{
log_warning("Tried to remove non ghost during ghost removal.");
return MakeResult(GameActions::Status::Disallowed, STR_CANT_REMOVE_THIS);
}
auto res = MakeResult();
res->Position = _loc;
res->Cost = MONEY(0, 0);
return res;
}
GameActions::Result::Ptr FootpathAdditionRemoveAction::Execute() const
{
auto tileElement = map_get_footpath_element(_loc);
auto pathElement = tileElement->AsPath();
if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST))
{
footpath_interrupt_peeps(_loc);
}
if (pathElement == nullptr)
{
log_error("Could not find path element.");
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REMOVE_THIS);
}
pathElement->SetAddition(0);
map_invalidate_tile_full(_loc);
auto res = MakeResult();
res->Position = _loc;
res->Cost = MONEY(0, 0);
return res;
}

View File

@ -0,0 +1,35 @@
/*****************************************************************************
* 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
#include "GameAction.h"
DEFINE_GAME_ACTION(FootpathAdditionRemoveAction, GAME_COMMAND_REMOVE_FOOTPATH_ADDITION, GameActions::Result)
{
private:
CoordsXYZ _loc;
public:
FootpathAdditionRemoveAction() = default;
FootpathAdditionRemoveAction(const CoordsXYZ& loc)
: _loc(loc)
{
}
void AcceptParameters(GameActionParameterVisitor & visitor) override;
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags();
}
void Serialise(DataSerialiser & stream) override;
GameActions::Result::Ptr Query() const override;
GameActions::Result::Ptr Execute() const override;
};

View File

@ -1,125 +0,0 @@
/*****************************************************************************
* 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
#include "../Cheats.h"
#include "../OpenRCT2.h"
#include "../core/MemoryStream.h"
#include "../interface/Window.h"
#include "../localisation/StringIds.h"
#include "../management/Finance.h"
#include "../world/Footpath.h"
#include "../world/Location.hpp"
#include "../world/Park.h"
#include "../world/Wall.h"
#include "GameAction.h"
DEFINE_GAME_ACTION(FootpathAdditionRemoveAction, GAME_COMMAND_REMOVE_FOOTPATH_ADDITION, GameActions::Result)
{
private:
CoordsXYZ _loc;
public:
FootpathAdditionRemoveAction() = default;
FootpathAdditionRemoveAction(const CoordsXYZ& loc)
: _loc(loc)
{
}
void AcceptParameters(GameActionParameterVisitor & visitor) override
{
visitor.Visit(_loc);
}
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags();
}
void Serialise(DataSerialiser & stream) override
{
GameAction::Serialise(stream);
stream << DS_TAG(_loc);
}
GameActions::Result::Ptr Query() const override
{
if (!LocationValid(_loc))
{
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REMOVE_THIS, STR_OFF_EDGE_OF_MAP);
}
if (!((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode) && !map_is_location_owned(_loc))
{
return MakeResult(GameActions::Status::Disallowed, STR_CANT_REMOVE_THIS, STR_LAND_NOT_OWNED_BY_PARK);
}
if (_loc.z < FootpathMinHeight)
{
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REMOVE_THIS, STR_TOO_LOW);
}
if (_loc.z > FootpathMaxHeight)
{
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REMOVE_THIS, STR_TOO_HIGH);
}
auto tileElement = map_get_footpath_element(_loc);
if (tileElement == nullptr)
{
log_warning("Could not find path element.");
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REMOVE_THIS);
}
auto pathElement = tileElement->AsPath();
if (pathElement == nullptr)
{
log_warning("Could not find path element.");
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REMOVE_THIS);
}
if (!pathElement->AdditionIsGhost() && (GetFlags() & GAME_COMMAND_FLAG_GHOST))
{
log_warning("Tried to remove non ghost during ghost removal.");
return MakeResult(GameActions::Status::Disallowed, STR_CANT_REMOVE_THIS);
}
auto res = MakeResult();
res->Position = _loc;
res->Cost = MONEY(0, 0);
return res;
}
GameActions::Result::Ptr Execute() const override
{
auto tileElement = map_get_footpath_element(_loc);
auto pathElement = tileElement->AsPath();
if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST))
{
footpath_interrupt_peeps(_loc);
}
if (pathElement == nullptr)
{
log_error("Could not find path element.");
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REMOVE_THIS);
}
pathElement->SetAddition(0);
map_invalidate_tile_full(_loc);
auto res = MakeResult();
res->Position = _loc;
res->Cost = MONEY(0, 0);
return res;
}
};

View File

@ -0,0 +1,450 @@
/*****************************************************************************
* 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.
*****************************************************************************/
#include "FootpathPlaceAction.h"
#include "../Cheats.h"
#include "../OpenRCT2.h"
#include "../core/MemoryStream.h"
#include "../interface/Window.h"
#include "../localisation/StringIds.h"
#include "../management/Finance.h"
#include "../world/Footpath.h"
#include "../world/Location.hpp"
#include "../world/Park.h"
#include "../world/Scenery.h"
#include "../world/Surface.h"
#include "../world/Wall.h"
void FootpathPlaceAction::AcceptParameters(GameActionParameterVisitor& visitor)
{
visitor.Visit(_loc);
visitor.Visit("object", _type);
visitor.Visit("direction", _direction);
visitor.Visit("slope", _slope);
}
void FootpathPlaceAction::Serialise(DataSerialiser& stream)
{
GameAction::Serialise(stream);
stream << DS_TAG(_loc) << DS_TAG(_slope) << DS_TAG(_type) << DS_TAG(_direction);
}
GameActions::Result::Ptr FootpathPlaceAction::Query() const
{
GameActions::Result::Ptr res = std::make_unique<GameActions::Result>();
res->Cost = 0;
res->Expenditure = ExpenditureType::Landscaping;
res->Position = _loc.ToTileCentre();
gFootpathGroundFlags = 0;
if (!LocationValid(_loc) || map_is_edge(_loc))
{
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_BUILD_FOOTPATH_HERE, STR_OFF_EDGE_OF_MAP);
}
if (!((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode) && !map_is_location_owned(_loc))
{
return MakeResult(GameActions::Status::Disallowed, STR_CANT_BUILD_FOOTPATH_HERE, STR_LAND_NOT_OWNED_BY_PARK);
}
if (_slope & SLOPE_IS_IRREGULAR_FLAG)
{
return MakeResult(GameActions::Status::Disallowed, STR_CANT_BUILD_FOOTPATH_HERE, STR_LAND_SLOPE_UNSUITABLE);
}
if (_loc.z < FootpathMinHeight)
{
return MakeResult(GameActions::Status::Disallowed, STR_CANT_BUILD_FOOTPATH_HERE, STR_TOO_LOW);
}
if (_loc.z > FootpathMaxHeight)
{
return MakeResult(GameActions::Status::Disallowed, STR_CANT_BUILD_FOOTPATH_HERE, STR_TOO_HIGH);
}
if (_direction != INVALID_DIRECTION && !direction_valid(_direction))
{
log_error("Direction invalid. direction = %u", _direction);
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_BUILD_FOOTPATH_HERE);
}
footpath_provisional_remove();
auto tileElement = map_get_footpath_element_slope(_loc, _slope);
if (tileElement == nullptr)
{
return ElementInsertQuery(std::move(res));
}
else
{
return ElementUpdateQuery(tileElement, std::move(res));
}
}
GameActions::Result::Ptr FootpathPlaceAction::Execute() const
{
GameActions::Result::Ptr res = std::make_unique<GameActions::Result>();
res->Cost = 0;
res->Expenditure = ExpenditureType::Landscaping;
res->Position = _loc.ToTileCentre();
if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST))
{
footpath_interrupt_peeps(_loc);
}
gFootpathGroundFlags = 0;
// Force ride construction to recheck area
_currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_RECHECK;
if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST))
{
if (_direction != INVALID_DIRECTION && !gCheatsDisableClearanceChecks)
{
// It is possible, let's remove walls between the old and new piece of path
auto zLow = _loc.z;
auto zHigh = zLow + PATH_CLEARANCE;
wall_remove_intersecting_walls(
{ _loc, zLow, zHigh + ((_slope & TILE_ELEMENT_SURFACE_RAISED_CORNERS_MASK) ? 16 : 0) },
direction_reverse(_direction));
wall_remove_intersecting_walls(
{ _loc.x - CoordsDirectionDelta[_direction].x, _loc.y - CoordsDirectionDelta[_direction].y, zLow, zHigh },
_direction);
}
}
auto tileElement = map_get_footpath_element_slope(_loc, _slope);
if (tileElement == nullptr)
{
return ElementInsertExecute(std::move(res));
}
else
{
return ElementUpdateExecute(tileElement, std::move(res));
}
}
GameActions::Result::Ptr FootpathPlaceAction::ElementUpdateQuery(PathElement* pathElement, GameActions::Result::Ptr res) const
{
const int32_t newFootpathType = (_type & (FOOTPATH_PROPERTIES_TYPE_MASK >> 4));
const bool newPathIsQueue = ((_type >> 7) == 1);
if (pathElement->GetSurfaceEntryIndex() != newFootpathType || pathElement->IsQueue() != newPathIsQueue)
{
res->Cost += MONEY(6, 00);
}
if (GetFlags() & GAME_COMMAND_FLAG_GHOST && !pathElement->IsGhost())
{
return MakeResult(GameActions::Status::Unknown, STR_CANT_BUILD_FOOTPATH_HERE);
}
return res;
}
GameActions::Result::Ptr FootpathPlaceAction::ElementUpdateExecute(PathElement* pathElement, GameActions::Result::Ptr res) const
{
const int32_t newFootpathType = (_type & (FOOTPATH_PROPERTIES_TYPE_MASK >> 4));
const bool newPathIsQueue = ((_type >> 7) == 1);
if (pathElement->GetSurfaceEntryIndex() != newFootpathType || pathElement->IsQueue() != newPathIsQueue)
{
res->Cost += MONEY(6, 00);
}
footpath_queue_chain_reset();
if (!(GetFlags() & GAME_COMMAND_FLAG_PATH_SCENERY))
{
footpath_remove_edges_at(_loc, reinterpret_cast<TileElement*>(pathElement));
}
pathElement->SetSurfaceEntryIndex(_type & ~FOOTPATH_ELEMENT_INSERT_QUEUE);
bool isQueue = _type & FOOTPATH_ELEMENT_INSERT_QUEUE;
pathElement->SetIsQueue(isQueue);
rct_scenery_entry* elem = pathElement->GetAdditionEntry();
if (elem != nullptr)
{
if (isQueue)
{
// remove any addition that isn't a TV or a lamp
if ((elem->path_bit.flags & PATH_BIT_FLAG_IS_QUEUE_SCREEN) == 0 && (elem->path_bit.flags & PATH_BIT_FLAG_LAMP) == 0)
{
pathElement->SetIsBroken(false);
pathElement->SetAddition(0);
}
}
else
{
// remove all TVs
if ((elem->path_bit.flags & PATH_BIT_FLAG_IS_QUEUE_SCREEN) != 0)
{
pathElement->SetIsBroken(false);
pathElement->SetAddition(0);
}
}
}
RemoveIntersectingWalls(pathElement);
return res;
}
GameActions::Result::Ptr FootpathPlaceAction::ElementInsertQuery(GameActions::Result::Ptr res) const
{
bool entrancePath = false, entranceIsSamePath = false;
if (!map_check_free_elements_and_reorganise(1))
{
return MakeResult(GameActions::Status::NoFreeElements, STR_CANT_BUILD_FOOTPATH_HERE);
}
res->Cost = MONEY(12, 00);
QuarterTile quarterTile{ 0b1111, 0 };
auto zLow = _loc.z;
auto zHigh = zLow + PATH_CLEARANCE;
if (_slope & FOOTPATH_PROPERTIES_FLAG_IS_SLOPED)
{
quarterTile = QuarterTile{ 0b1111, 0b1100 }.Rotate(_slope & TILE_ELEMENT_DIRECTION_MASK);
zHigh += PATH_HEIGHT_STEP;
}
auto entranceElement = map_get_park_entrance_element_at(_loc, false);
// Make sure the entrance part is the middle
if (entranceElement != nullptr && (entranceElement->GetSequenceIndex()) == 0)
{
entrancePath = true;
// Make the price the same as replacing a path
if (entranceElement->GetPathType() == (_type & 0xF))
entranceIsSamePath = true;
else
res->Cost -= MONEY(6, 00);
}
// Do not attempt to build a crossing with a queue or a sloped.
uint8_t crossingMode = (_type & FOOTPATH_ELEMENT_INSERT_QUEUE) || (_slope != TILE_ELEMENT_SLOPE_FLAT)
? CREATE_CROSSING_MODE_NONE
: CREATE_CROSSING_MODE_PATH_OVER_TRACK;
if (!entrancePath
&& !map_can_construct_with_clear_at(
{ _loc, zLow, zHigh }, &map_place_non_scenery_clear_func, quarterTile, GetFlags(), &res->Cost, crossingMode))
{
return MakeResult(
GameActions::Status::NoClearance, STR_CANT_BUILD_FOOTPATH_HERE, gGameCommandErrorText, gCommonFormatArgs);
}
gFootpathGroundFlags = gMapGroundFlags;
if (!gCheatsDisableClearanceChecks && (gMapGroundFlags & ELEMENT_IS_UNDERWATER))
{
return MakeResult(GameActions::Status::Disallowed, STR_CANT_BUILD_FOOTPATH_HERE, STR_CANT_BUILD_THIS_UNDERWATER);
}
auto surfaceElement = map_get_surface_element_at(_loc);
if (surfaceElement == nullptr)
{
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_BUILD_FOOTPATH_HERE);
}
int32_t supportHeight = zLow - surfaceElement->GetBaseZ();
res->Cost += supportHeight < 0 ? MONEY(20, 00) : (supportHeight / PATH_HEIGHT_STEP) * MONEY(5, 00);
// Prevent the place sound from being spammed
if (entranceIsSamePath)
res->Cost = 0;
return res;
}
GameActions::Result::Ptr FootpathPlaceAction::ElementInsertExecute(GameActions::Result::Ptr res) const
{
bool entrancePath = false, entranceIsSamePath = false;
if (!(GetFlags() & (GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_GHOST)))
{
footpath_remove_litter(_loc);
}
res->Cost = MONEY(12, 00);
QuarterTile quarterTile{ 0b1111, 0 };
auto zLow = _loc.z;
auto zHigh = zLow + PATH_CLEARANCE;
if (_slope & FOOTPATH_PROPERTIES_FLAG_IS_SLOPED)
{
quarterTile = QuarterTile{ 0b1111, 0b1100 }.Rotate(_slope & TILE_ELEMENT_DIRECTION_MASK);
zHigh += PATH_HEIGHT_STEP;
}
auto entranceElement = map_get_park_entrance_element_at(_loc, false);
// Make sure the entrance part is the middle
if (entranceElement != nullptr && (entranceElement->GetSequenceIndex()) == 0)
{
entrancePath = true;
// Make the price the same as replacing a path
if (entranceElement->GetPathType() == (_type & 0xF))
entranceIsSamePath = true;
else
res->Cost -= MONEY(6, 00);
}
// Do not attempt to build a crossing with a queue or a sloped.
uint8_t crossingMode = (_type & FOOTPATH_ELEMENT_INSERT_QUEUE) || (_slope != TILE_ELEMENT_SLOPE_FLAT)
? CREATE_CROSSING_MODE_NONE
: CREATE_CROSSING_MODE_PATH_OVER_TRACK;
if (!entrancePath
&& !map_can_construct_with_clear_at(
{ _loc, zLow, zHigh }, &map_place_non_scenery_clear_func, quarterTile, GAME_COMMAND_FLAG_APPLY | GetFlags(),
&res->Cost, crossingMode))
{
return MakeResult(
GameActions::Status::NoClearance, STR_CANT_BUILD_FOOTPATH_HERE, gGameCommandErrorText, gCommonFormatArgs);
}
gFootpathGroundFlags = gMapGroundFlags;
auto surfaceElement = map_get_surface_element_at(_loc);
if (surfaceElement == nullptr)
{
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_BUILD_FOOTPATH_HERE);
}
int32_t supportHeight = zLow - surfaceElement->GetBaseZ();
res->Cost += supportHeight < 0 ? MONEY(20, 00) : (supportHeight / PATH_HEIGHT_STEP) * MONEY(5, 00);
if (entrancePath)
{
if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST) && !entranceIsSamePath)
{
// Set the path type but make sure it's not a queue as that will not show up
entranceElement->SetPathType(_type & 0x7F);
map_invalidate_tile_full(_loc);
}
}
else
{
auto tileElement = tile_element_insert(_loc, 0b1111);
assert(tileElement != nullptr);
tileElement->SetType(TILE_ELEMENT_TYPE_PATH);
PathElement* pathElement = tileElement->AsPath();
pathElement->SetClearanceZ(zHigh);
pathElement->SetSurfaceEntryIndex(_type & ~FOOTPATH_ELEMENT_INSERT_QUEUE);
pathElement->SetSlopeDirection(_slope & FOOTPATH_PROPERTIES_SLOPE_DIRECTION_MASK);
if (_slope & FOOTPATH_PROPERTIES_FLAG_IS_SLOPED)
{
pathElement->SetSloped(true);
}
if (_type & FOOTPATH_ELEMENT_INSERT_QUEUE)
{
pathElement->SetIsQueue(true);
}
pathElement->SetAddition(0);
pathElement->SetRideIndex(RIDE_ID_NULL);
pathElement->SetAdditionStatus(255);
pathElement->SetIsBroken(false);
if (GetFlags() & GAME_COMMAND_FLAG_GHOST)
{
pathElement->SetGhost(true);
}
footpath_queue_chain_reset();
if (!(GetFlags() & GAME_COMMAND_FLAG_PATH_SCENERY))
{
footpath_remove_edges_at(_loc, tileElement);
}
if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !(GetFlags() & GAME_COMMAND_FLAG_GHOST))
{
AutomaticallySetPeepSpawn();
}
RemoveIntersectingWalls(pathElement);
}
// Prevent the place sound from being spammed
if (entranceIsSamePath)
res->Cost = 0;
return res;
}
/**
*
* rct2: 0x006A65AD
*/
void FootpathPlaceAction::AutomaticallySetPeepSpawn() const
{
uint8_t direction = 0;
if (_loc.x != 32)
{
direction++;
if (_loc.y != gMapSizeUnits - 32)
{
direction++;
if (_loc.x != gMapSizeUnits - 32)
{
direction++;
if (_loc.y != 32)
return;
}
}
}
if (gPeepSpawns.empty())
{
gPeepSpawns.emplace_back();
}
PeepSpawn* peepSpawn = &gPeepSpawns[0];
peepSpawn->x = _loc.x + (DirectionOffsets[direction].x * 15) + 16;
peepSpawn->y = _loc.y + (DirectionOffsets[direction].y * 15) + 16;
peepSpawn->direction = direction;
peepSpawn->z = _loc.z;
}
void FootpathPlaceAction::RemoveIntersectingWalls(PathElement* pathElement) const
{
if (pathElement->IsSloped() && !(GetFlags() & GAME_COMMAND_FLAG_GHOST))
{
auto direction = pathElement->GetSlopeDirection();
int32_t z = pathElement->GetBaseZ();
wall_remove_intersecting_walls({ _loc, z, z + (6 * COORDS_Z_STEP) }, direction_reverse(direction));
wall_remove_intersecting_walls({ _loc, z, z + (6 * COORDS_Z_STEP) }, direction);
// Removing walls may have made the pointer invalid, so find it again
auto tileElement = map_get_footpath_element(CoordsXYZ(_loc, z));
if (tileElement == nullptr)
{
log_error("Something went wrong. Could not refind footpath.");
return;
}
pathElement = tileElement->AsPath();
}
if (!(GetFlags() & GAME_COMMAND_FLAG_PATH_SCENERY))
footpath_connect_edges(_loc, reinterpret_cast<TileElement*>(pathElement), GetFlags());
footpath_update_queue_chains();
map_invalidate_tile_full(_loc);
}
PathElement* FootpathPlaceAction::map_get_footpath_element_slope(const CoordsXYZ& footpathPos, int32_t slope) const
{
TileElement* tileElement;
bool isSloped = slope & FOOTPATH_PROPERTIES_FLAG_IS_SLOPED;
tileElement = map_get_first_element_at(footpathPos);
do
{
if (tileElement == nullptr)
break;
if (tileElement->GetType() == TILE_ELEMENT_TYPE_PATH && tileElement->GetBaseZ() == footpathPos.z
&& (tileElement->AsPath()->IsSloped() == isSloped)
&& (tileElement->AsPath()->GetSlopeDirection() == (slope & FOOTPATH_PROPERTIES_SLOPE_DIRECTION_MASK)))
{
return tileElement->AsPath();
}
} while (!(tileElement++)->IsLastForTile());
return nullptr;
}

View File

@ -0,0 +1,52 @@
/*****************************************************************************
* 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
#include "../world/Footpath.h"
#include "GameAction.h"
DEFINE_GAME_ACTION(FootpathPlaceAction, GAME_COMMAND_PLACE_PATH, GameActions::Result)
{
private:
CoordsXYZ _loc;
uint8_t _slope{};
ObjectEntryIndex _type{};
Direction _direction{ INVALID_DIRECTION };
public:
FootpathPlaceAction() = default;
FootpathPlaceAction(const CoordsXYZ& loc, uint8_t slope, ObjectEntryIndex type, Direction direction = INVALID_DIRECTION)
: _loc(loc)
, _slope(slope)
, _type(type)
, _direction(direction)
{
}
void AcceptParameters(GameActionParameterVisitor & visitor) override;
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags();
}
void Serialise(DataSerialiser & stream) override;
GameActions::Result::Ptr Query() const override;
GameActions::Result::Ptr Execute() const override;
private:
GameActions::Result::Ptr ElementUpdateQuery(PathElement * pathElement, GameActions::Result::Ptr res) const;
GameActions::Result::Ptr ElementUpdateExecute(PathElement * pathElement, GameActions::Result::Ptr res) const;
GameActions::Result::Ptr ElementInsertQuery(GameActions::Result::Ptr res) const;
GameActions::Result::Ptr ElementInsertExecute(GameActions::Result::Ptr res) const;
void AutomaticallySetPeepSpawn() const;
void RemoveIntersectingWalls(PathElement * pathElement) const;
PathElement* map_get_footpath_element_slope(const CoordsXYZ& footpathPos, int32_t slope) const;
};

View File

@ -1,476 +0,0 @@
/*****************************************************************************
* 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
#include "../Cheats.h"
#include "../OpenRCT2.h"
#include "../core/MemoryStream.h"
#include "../interface/Window.h"
#include "../localisation/StringIds.h"
#include "../management/Finance.h"
#include "../world/Footpath.h"
#include "../world/Location.hpp"
#include "../world/Park.h"
#include "../world/Scenery.h"
#include "../world/Surface.h"
#include "../world/Wall.h"
#include "GameAction.h"
DEFINE_GAME_ACTION(FootpathPlaceAction, GAME_COMMAND_PLACE_PATH, GameActions::Result)
{
private:
CoordsXYZ _loc;
uint8_t _slope{};
ObjectEntryIndex _type{};
Direction _direction{ INVALID_DIRECTION };
public:
FootpathPlaceAction() = default;
FootpathPlaceAction(const CoordsXYZ& loc, uint8_t slope, ObjectEntryIndex type, Direction direction = INVALID_DIRECTION)
: _loc(loc)
, _slope(slope)
, _type(type)
, _direction(direction)
{
}
void AcceptParameters(GameActionParameterVisitor & visitor) override
{
visitor.Visit(_loc);
visitor.Visit("object", _type);
visitor.Visit("direction", _direction);
visitor.Visit("slope", _slope);
}
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags();
}
void Serialise(DataSerialiser & stream) override
{
GameAction::Serialise(stream);
stream << DS_TAG(_loc) << DS_TAG(_slope) << DS_TAG(_type) << DS_TAG(_direction);
}
GameActions::Result::Ptr Query() const override
{
GameActions::Result::Ptr res = std::make_unique<GameActions::Result>();
res->Cost = 0;
res->Expenditure = ExpenditureType::Landscaping;
res->Position = _loc.ToTileCentre();
gFootpathGroundFlags = 0;
if (!LocationValid(_loc) || map_is_edge(_loc))
{
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_BUILD_FOOTPATH_HERE, STR_OFF_EDGE_OF_MAP);
}
if (!((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode) && !map_is_location_owned(_loc))
{
return MakeResult(GameActions::Status::Disallowed, STR_CANT_BUILD_FOOTPATH_HERE, STR_LAND_NOT_OWNED_BY_PARK);
}
if (_slope & SLOPE_IS_IRREGULAR_FLAG)
{
return MakeResult(GameActions::Status::Disallowed, STR_CANT_BUILD_FOOTPATH_HERE, STR_LAND_SLOPE_UNSUITABLE);
}
if (_loc.z < FootpathMinHeight)
{
return MakeResult(GameActions::Status::Disallowed, STR_CANT_BUILD_FOOTPATH_HERE, STR_TOO_LOW);
}
if (_loc.z > FootpathMaxHeight)
{
return MakeResult(GameActions::Status::Disallowed, STR_CANT_BUILD_FOOTPATH_HERE, STR_TOO_HIGH);
}
if (_direction != INVALID_DIRECTION && !direction_valid(_direction))
{
log_error("Direction invalid. direction = %u", _direction);
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_BUILD_FOOTPATH_HERE);
}
footpath_provisional_remove();
auto tileElement = map_get_footpath_element_slope(_loc, _slope);
if (tileElement == nullptr)
{
return ElementInsertQuery(std::move(res));
}
else
{
return ElementUpdateQuery(tileElement, std::move(res));
}
}
GameActions::Result::Ptr Execute() const override
{
GameActions::Result::Ptr res = std::make_unique<GameActions::Result>();
res->Cost = 0;
res->Expenditure = ExpenditureType::Landscaping;
res->Position = _loc.ToTileCentre();
if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST))
{
footpath_interrupt_peeps(_loc);
}
gFootpathGroundFlags = 0;
// Force ride construction to recheck area
_currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_RECHECK;
if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST))
{
if (_direction != INVALID_DIRECTION && !gCheatsDisableClearanceChecks)
{
// It is possible, let's remove walls between the old and new piece of path
auto zLow = _loc.z;
auto zHigh = zLow + PATH_CLEARANCE;
wall_remove_intersecting_walls(
{ _loc, zLow, zHigh + ((_slope & TILE_ELEMENT_SURFACE_RAISED_CORNERS_MASK) ? 16 : 0) },
direction_reverse(_direction));
wall_remove_intersecting_walls(
{ _loc.x - CoordsDirectionDelta[_direction].x, _loc.y - CoordsDirectionDelta[_direction].y, zLow, zHigh },
_direction);
}
}
auto tileElement = map_get_footpath_element_slope(_loc, _slope);
if (tileElement == nullptr)
{
return ElementInsertExecute(std::move(res));
}
else
{
return ElementUpdateExecute(tileElement, std::move(res));
}
}
private:
GameActions::Result::Ptr ElementUpdateQuery(PathElement * pathElement, GameActions::Result::Ptr res) const
{
const int32_t newFootpathType = (_type & (FOOTPATH_PROPERTIES_TYPE_MASK >> 4));
const bool newPathIsQueue = ((_type >> 7) == 1);
if (pathElement->GetSurfaceEntryIndex() != newFootpathType || pathElement->IsQueue() != newPathIsQueue)
{
res->Cost += MONEY(6, 00);
}
if (GetFlags() & GAME_COMMAND_FLAG_GHOST && !pathElement->IsGhost())
{
return MakeResult(GameActions::Status::Unknown, STR_CANT_BUILD_FOOTPATH_HERE);
}
return res;
}
GameActions::Result::Ptr ElementUpdateExecute(PathElement * pathElement, GameActions::Result::Ptr res) const
{
const int32_t newFootpathType = (_type & (FOOTPATH_PROPERTIES_TYPE_MASK >> 4));
const bool newPathIsQueue = ((_type >> 7) == 1);
if (pathElement->GetSurfaceEntryIndex() != newFootpathType || pathElement->IsQueue() != newPathIsQueue)
{
res->Cost += MONEY(6, 00);
}
footpath_queue_chain_reset();
if (!(GetFlags() & GAME_COMMAND_FLAG_PATH_SCENERY))
{
footpath_remove_edges_at(_loc, reinterpret_cast<TileElement*>(pathElement));
}
pathElement->SetSurfaceEntryIndex(_type & ~FOOTPATH_ELEMENT_INSERT_QUEUE);
bool isQueue = _type & FOOTPATH_ELEMENT_INSERT_QUEUE;
pathElement->SetIsQueue(isQueue);
rct_scenery_entry* elem = pathElement->GetAdditionEntry();
if (elem != nullptr)
{
if (isQueue)
{
// remove any addition that isn't a TV or a lamp
if ((elem->path_bit.flags & PATH_BIT_FLAG_IS_QUEUE_SCREEN) == 0
&& (elem->path_bit.flags & PATH_BIT_FLAG_LAMP) == 0)
{
pathElement->SetIsBroken(false);
pathElement->SetAddition(0);
}
}
else
{
// remove all TVs
if ((elem->path_bit.flags & PATH_BIT_FLAG_IS_QUEUE_SCREEN) != 0)
{
pathElement->SetIsBroken(false);
pathElement->SetAddition(0);
}
}
}
RemoveIntersectingWalls(pathElement);
return res;
}
GameActions::Result::Ptr ElementInsertQuery(GameActions::Result::Ptr res) const
{
bool entrancePath = false, entranceIsSamePath = false;
if (!map_check_free_elements_and_reorganise(1))
{
return MakeResult(GameActions::Status::NoFreeElements, STR_CANT_BUILD_FOOTPATH_HERE);
}
res->Cost = MONEY(12, 00);
QuarterTile quarterTile{ 0b1111, 0 };
auto zLow = _loc.z;
auto zHigh = zLow + PATH_CLEARANCE;
if (_slope & FOOTPATH_PROPERTIES_FLAG_IS_SLOPED)
{
quarterTile = QuarterTile{ 0b1111, 0b1100 }.Rotate(_slope & TILE_ELEMENT_DIRECTION_MASK);
zHigh += PATH_HEIGHT_STEP;
}
auto entranceElement = map_get_park_entrance_element_at(_loc, false);
// Make sure the entrance part is the middle
if (entranceElement != nullptr && (entranceElement->GetSequenceIndex()) == 0)
{
entrancePath = true;
// Make the price the same as replacing a path
if (entranceElement->GetPathType() == (_type & 0xF))
entranceIsSamePath = true;
else
res->Cost -= MONEY(6, 00);
}
// Do not attempt to build a crossing with a queue or a sloped.
uint8_t crossingMode = (_type & FOOTPATH_ELEMENT_INSERT_QUEUE) || (_slope != TILE_ELEMENT_SLOPE_FLAT)
? CREATE_CROSSING_MODE_NONE
: CREATE_CROSSING_MODE_PATH_OVER_TRACK;
if (!entrancePath
&& !map_can_construct_with_clear_at(
{ _loc, zLow, zHigh }, &map_place_non_scenery_clear_func, quarterTile, GetFlags(), &res->Cost, crossingMode))
{
return MakeResult(
GameActions::Status::NoClearance, STR_CANT_BUILD_FOOTPATH_HERE, gGameCommandErrorText, gCommonFormatArgs);
}
gFootpathGroundFlags = gMapGroundFlags;
if (!gCheatsDisableClearanceChecks && (gMapGroundFlags & ELEMENT_IS_UNDERWATER))
{
return MakeResult(GameActions::Status::Disallowed, STR_CANT_BUILD_FOOTPATH_HERE, STR_CANT_BUILD_THIS_UNDERWATER);
}
auto surfaceElement = map_get_surface_element_at(_loc);
if (surfaceElement == nullptr)
{
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_BUILD_FOOTPATH_HERE);
}
int32_t supportHeight = zLow - surfaceElement->GetBaseZ();
res->Cost += supportHeight < 0 ? MONEY(20, 00) : (supportHeight / PATH_HEIGHT_STEP) * MONEY(5, 00);
// Prevent the place sound from being spammed
if (entranceIsSamePath)
res->Cost = 0;
return res;
}
GameActions::Result::Ptr ElementInsertExecute(GameActions::Result::Ptr res) const
{
bool entrancePath = false, entranceIsSamePath = false;
if (!(GetFlags() & (GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_GHOST)))
{
footpath_remove_litter(_loc);
}
res->Cost = MONEY(12, 00);
QuarterTile quarterTile{ 0b1111, 0 };
auto zLow = _loc.z;
auto zHigh = zLow + PATH_CLEARANCE;
if (_slope & FOOTPATH_PROPERTIES_FLAG_IS_SLOPED)
{
quarterTile = QuarterTile{ 0b1111, 0b1100 }.Rotate(_slope & TILE_ELEMENT_DIRECTION_MASK);
zHigh += PATH_HEIGHT_STEP;
}
auto entranceElement = map_get_park_entrance_element_at(_loc, false);
// Make sure the entrance part is the middle
if (entranceElement != nullptr && (entranceElement->GetSequenceIndex()) == 0)
{
entrancePath = true;
// Make the price the same as replacing a path
if (entranceElement->GetPathType() == (_type & 0xF))
entranceIsSamePath = true;
else
res->Cost -= MONEY(6, 00);
}
// Do not attempt to build a crossing with a queue or a sloped.
uint8_t crossingMode = (_type & FOOTPATH_ELEMENT_INSERT_QUEUE) || (_slope != TILE_ELEMENT_SLOPE_FLAT)
? CREATE_CROSSING_MODE_NONE
: CREATE_CROSSING_MODE_PATH_OVER_TRACK;
if (!entrancePath
&& !map_can_construct_with_clear_at(
{ _loc, zLow, zHigh }, &map_place_non_scenery_clear_func, quarterTile, GAME_COMMAND_FLAG_APPLY | GetFlags(),
&res->Cost, crossingMode))
{
return MakeResult(
GameActions::Status::NoClearance, STR_CANT_BUILD_FOOTPATH_HERE, gGameCommandErrorText, gCommonFormatArgs);
}
gFootpathGroundFlags = gMapGroundFlags;
auto surfaceElement = map_get_surface_element_at(_loc);
if (surfaceElement == nullptr)
{
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_BUILD_FOOTPATH_HERE);
}
int32_t supportHeight = zLow - surfaceElement->GetBaseZ();
res->Cost += supportHeight < 0 ? MONEY(20, 00) : (supportHeight / PATH_HEIGHT_STEP) * MONEY(5, 00);
if (entrancePath)
{
if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST) && !entranceIsSamePath)
{
// Set the path type but make sure it's not a queue as that will not show up
entranceElement->SetPathType(_type & 0x7F);
map_invalidate_tile_full(_loc);
}
}
else
{
auto tileElement = tile_element_insert(_loc, 0b1111);
assert(tileElement != nullptr);
tileElement->SetType(TILE_ELEMENT_TYPE_PATH);
PathElement* pathElement = tileElement->AsPath();
pathElement->SetClearanceZ(zHigh);
pathElement->SetSurfaceEntryIndex(_type & ~FOOTPATH_ELEMENT_INSERT_QUEUE);
pathElement->SetSlopeDirection(_slope & FOOTPATH_PROPERTIES_SLOPE_DIRECTION_MASK);
if (_slope & FOOTPATH_PROPERTIES_FLAG_IS_SLOPED)
{
pathElement->SetSloped(true);
}
if (_type & FOOTPATH_ELEMENT_INSERT_QUEUE)
{
pathElement->SetIsQueue(true);
}
pathElement->SetAddition(0);
pathElement->SetRideIndex(RIDE_ID_NULL);
pathElement->SetAdditionStatus(255);
pathElement->SetIsBroken(false);
if (GetFlags() & GAME_COMMAND_FLAG_GHOST)
{
pathElement->SetGhost(true);
}
footpath_queue_chain_reset();
if (!(GetFlags() & GAME_COMMAND_FLAG_PATH_SCENERY))
{
footpath_remove_edges_at(_loc, tileElement);
}
if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !(GetFlags() & GAME_COMMAND_FLAG_GHOST))
{
AutomaticallySetPeepSpawn();
}
RemoveIntersectingWalls(pathElement);
}
// Prevent the place sound from being spammed
if (entranceIsSamePath)
res->Cost = 0;
return res;
}
/**
*
* rct2: 0x006A65AD
*/
void AutomaticallySetPeepSpawn() const
{
uint8_t direction = 0;
if (_loc.x != 32)
{
direction++;
if (_loc.y != gMapSizeUnits - 32)
{
direction++;
if (_loc.x != gMapSizeUnits - 32)
{
direction++;
if (_loc.y != 32)
return;
}
}
}
if (gPeepSpawns.empty())
{
gPeepSpawns.emplace_back();
}
PeepSpawn* peepSpawn = &gPeepSpawns[0];
peepSpawn->x = _loc.x + (DirectionOffsets[direction].x * 15) + 16;
peepSpawn->y = _loc.y + (DirectionOffsets[direction].y * 15) + 16;
peepSpawn->direction = direction;
peepSpawn->z = _loc.z;
}
void RemoveIntersectingWalls(PathElement * pathElement) const
{
if (pathElement->IsSloped() && !(GetFlags() & GAME_COMMAND_FLAG_GHOST))
{
auto direction = pathElement->GetSlopeDirection();
int32_t z = pathElement->GetBaseZ();
wall_remove_intersecting_walls({ _loc, z, z + (6 * COORDS_Z_STEP) }, direction_reverse(direction));
wall_remove_intersecting_walls({ _loc, z, z + (6 * COORDS_Z_STEP) }, direction);
// Removing walls may have made the pointer invalid, so find it again
auto tileElement = map_get_footpath_element(CoordsXYZ(_loc, z));
if (tileElement == nullptr)
{
log_error("Something went wrong. Could not refind footpath.");
return;
}
pathElement = tileElement->AsPath();
}
if (!(GetFlags() & GAME_COMMAND_FLAG_PATH_SCENERY))
footpath_connect_edges(_loc, reinterpret_cast<TileElement*>(pathElement), GetFlags());
footpath_update_queue_chains();
map_invalidate_tile_full(_loc);
}
PathElement* map_get_footpath_element_slope(const CoordsXYZ& footpathPos, int32_t slope) const
{
TileElement* tileElement;
bool isSloped = slope & FOOTPATH_PROPERTIES_FLAG_IS_SLOPED;
tileElement = map_get_first_element_at(footpathPos);
do
{
if (tileElement == nullptr)
break;
if (tileElement->GetType() == TILE_ELEMENT_TYPE_PATH && tileElement->GetBaseZ() == footpathPos.z
&& (tileElement->AsPath()->IsSloped() == isSloped)
&& (tileElement->AsPath()->GetSlopeDirection() == (slope & FOOTPATH_PROPERTIES_SLOPE_DIRECTION_MASK)))
{
return tileElement->AsPath();
}
} while (!(tileElement++)->IsLastForTile());
return nullptr;
}
};

View File

@ -0,0 +1,252 @@
/*****************************************************************************
* 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.
*****************************************************************************/
#include "FootpathPlaceFromTrackAction.h"
#include "../Cheats.h"
#include "../OpenRCT2.h"
#include "../core/MemoryStream.h"
#include "../interface/Window.h"
#include "../localisation/StringIds.h"
#include "../management/Finance.h"
#include "../world/Footpath.h"
#include "../world/Location.hpp"
#include "../world/Park.h"
#include "../world/Surface.h"
#include "../world/Wall.h"
void FootpathPlaceFromTrackAction::Serialise(DataSerialiser& stream)
{
GameAction::Serialise(stream);
stream << DS_TAG(_loc) << DS_TAG(_slope) << DS_TAG(_type) << DS_TAG(_edges);
}
GameActions::Result::Ptr FootpathPlaceFromTrackAction::Query() const
{
GameActions::Result::Ptr res = std::make_unique<GameActions::Result>();
res->Cost = 0;
res->Expenditure = ExpenditureType::Landscaping;
res->Position = _loc.ToTileCentre();
gFootpathGroundFlags = 0;
if (!LocationValid(_loc) || map_is_edge(_loc))
{
return MakeResult(
GameActions::Status::InvalidParameters, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, STR_OFF_EDGE_OF_MAP);
}
if (!((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode) && !map_is_location_owned(_loc))
{
return MakeResult(
GameActions::Status::Disallowed, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, STR_LAND_NOT_OWNED_BY_PARK);
}
if (_loc.z < FootpathMinHeight)
{
return MakeResult(GameActions::Status::Disallowed, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, STR_TOO_LOW);
}
if (_loc.z > FootpathMaxHeight)
{
return MakeResult(GameActions::Status::Disallowed, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, STR_TOO_HIGH);
}
return ElementInsertQuery(std::move(res));
}
GameActions::Result::Ptr FootpathPlaceFromTrackAction::Execute() const
{
GameActions::Result::Ptr res = std::make_unique<GameActions::Result>();
res->Cost = 0;
res->Expenditure = ExpenditureType::Landscaping;
res->Position = _loc.ToTileCentre();
if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST))
{
footpath_interrupt_peeps(_loc);
}
gFootpathGroundFlags = 0;
// Force ride construction to recheck area
_currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_RECHECK;
return ElementInsertExecute(std::move(res));
}
GameActions::Result::Ptr FootpathPlaceFromTrackAction::ElementInsertQuery(GameActions::Result::Ptr res) const
{
bool entrancePath = false, entranceIsSamePath = false;
if (!map_check_free_elements_and_reorganise(1))
{
return MakeResult(GameActions::Status::NoFreeElements, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE);
}
res->Cost = MONEY(12, 00);
QuarterTile quarterTile{ 0b1111, 0 };
auto zLow = _loc.z;
auto zHigh = zLow + PATH_CLEARANCE;
if (_slope & FOOTPATH_PROPERTIES_FLAG_IS_SLOPED)
{
quarterTile = QuarterTile{ 0b1111, 0b1100 }.Rotate(_slope & TILE_ELEMENT_DIRECTION_MASK);
zHigh += PATH_HEIGHT_STEP;
}
auto entranceElement = map_get_park_entrance_element_at(_loc, false);
// Make sure the entrance part is the middle
if (entranceElement != nullptr && (entranceElement->GetSequenceIndex()) == 0)
{
entrancePath = true;
// Make the price the same as replacing a path
if (entranceElement->GetPathType() == (_type & 0xF))
entranceIsSamePath = true;
else
res->Cost -= MONEY(6, 00);
}
// Do not attempt to build a crossing with a queue or a sloped.
uint8_t crossingMode = (_type & FOOTPATH_ELEMENT_INSERT_QUEUE) || (_slope != TILE_ELEMENT_SLOPE_FLAT)
? CREATE_CROSSING_MODE_NONE
: CREATE_CROSSING_MODE_PATH_OVER_TRACK;
if (!entrancePath
&& !map_can_construct_with_clear_at(
{ _loc, zLow, zHigh }, &map_place_non_scenery_clear_func, quarterTile, GetFlags(), &res->Cost, crossingMode))
{
return MakeResult(
GameActions::Status::NoClearance, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, gGameCommandErrorText,
gCommonFormatArgs);
}
gFootpathGroundFlags = gMapGroundFlags;
if (!gCheatsDisableClearanceChecks && (gMapGroundFlags & ELEMENT_IS_UNDERWATER))
{
return MakeResult(
GameActions::Status::Disallowed, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, STR_CANT_BUILD_THIS_UNDERWATER);
}
auto surfaceElement = map_get_surface_element_at(_loc);
if (surfaceElement == nullptr)
{
return MakeResult(GameActions::Status::InvalidParameters, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE);
}
int32_t supportHeight = zLow - surfaceElement->GetBaseZ();
res->Cost += supportHeight < 0 ? MONEY(20, 00) : (supportHeight / PATH_HEIGHT_STEP) * MONEY(5, 00);
// Prevent the place sound from being spammed
if (entranceIsSamePath)
res->Cost = 0;
return res;
}
GameActions::Result::Ptr FootpathPlaceFromTrackAction::ElementInsertExecute(GameActions::Result::Ptr res) const
{
bool entrancePath = false, entranceIsSamePath = false;
if (!(GetFlags() & (GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_GHOST)))
{
footpath_remove_litter(_loc);
}
res->Cost = MONEY(12, 00);
QuarterTile quarterTile{ 0b1111, 0 };
auto zLow = _loc.z;
auto zHigh = zLow + PATH_CLEARANCE;
if (_slope & FOOTPATH_PROPERTIES_FLAG_IS_SLOPED)
{
quarterTile = QuarterTile{ 0b1111, 0b1100 }.Rotate(_slope & TILE_ELEMENT_DIRECTION_MASK);
zHigh += PATH_HEIGHT_STEP;
}
auto entranceElement = map_get_park_entrance_element_at(_loc, false);
// Make sure the entrance part is the middle
if (entranceElement != nullptr && (entranceElement->GetSequenceIndex()) == 0)
{
entrancePath = true;
// Make the price the same as replacing a path
if (entranceElement->GetPathType() == (_type & 0xF))
entranceIsSamePath = true;
else
res->Cost -= MONEY(6, 00);
}
// Do not attempt to build a crossing with a queue or a sloped.
uint8_t crossingMode = (_type & FOOTPATH_ELEMENT_INSERT_QUEUE) || (_slope != TILE_ELEMENT_SLOPE_FLAT)
? CREATE_CROSSING_MODE_NONE
: CREATE_CROSSING_MODE_PATH_OVER_TRACK;
if (!entrancePath
&& !map_can_construct_with_clear_at(
{ _loc, zLow, zHigh }, &map_place_non_scenery_clear_func, quarterTile, GAME_COMMAND_FLAG_APPLY | GetFlags(),
&res->Cost, crossingMode))
{
return MakeResult(
GameActions::Status::NoClearance, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, gGameCommandErrorText,
gCommonFormatArgs);
}
gFootpathGroundFlags = gMapGroundFlags;
auto surfaceElement = map_get_surface_element_at(_loc);
if (surfaceElement == nullptr)
{
return MakeResult(GameActions::Status::InvalidParameters, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE);
}
int32_t supportHeight = zLow - surfaceElement->GetBaseZ();
res->Cost += supportHeight < 0 ? MONEY(20, 00) : (supportHeight / PATH_HEIGHT_STEP) * MONEY(5, 00);
if (entrancePath)
{
if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST) && !entranceIsSamePath)
{
// Set the path type but make sure it's not a queue as that will not show up
entranceElement->SetPathType(_type & 0x7F);
map_invalidate_tile_full(_loc);
}
}
else
{
auto tileElement = tile_element_insert(_loc, 0b1111);
assert(tileElement != nullptr);
tileElement->SetType(TILE_ELEMENT_TYPE_PATH);
PathElement* pathElement = tileElement->AsPath();
pathElement->SetClearanceZ(zHigh);
pathElement->SetSurfaceEntryIndex(_type & ~FOOTPATH_ELEMENT_INSERT_QUEUE);
pathElement->SetSlopeDirection(_slope & FOOTPATH_PROPERTIES_SLOPE_DIRECTION_MASK);
if (_slope & FOOTPATH_PROPERTIES_FLAG_IS_SLOPED)
{
pathElement->SetSloped(true);
}
if (_type & FOOTPATH_ELEMENT_INSERT_QUEUE)
{
pathElement->SetIsQueue(true);
}
pathElement->SetAddition(0);
pathElement->SetRideIndex(RIDE_ID_NULL);
pathElement->SetAdditionStatus(255);
pathElement->SetIsBroken(false);
pathElement->SetEdges(_edges);
pathElement->SetCorners(0);
if (GetFlags() & GAME_COMMAND_FLAG_GHOST)
{
pathElement->SetGhost(true);
}
map_invalidate_tile_full(_loc);
}
// Prevent the place sound from being spammed
if (entranceIsSamePath)
res->Cost = 0;
return res;
}

View File

@ -0,0 +1,44 @@
/*****************************************************************************
* 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
#include "GameAction.h"
DEFINE_GAME_ACTION(FootpathPlaceFromTrackAction, GAME_COMMAND_PLACE_PATH_FROM_TRACK, GameActions::Result)
{
private:
CoordsXYZ _loc;
uint8_t _slope{};
ObjectEntryIndex _type{};
uint8_t _edges{};
public:
FootpathPlaceFromTrackAction() = default;
FootpathPlaceFromTrackAction(const CoordsXYZ& loc, uint8_t slope, ObjectEntryIndex type, uint8_t edges)
: _loc(loc)
, _slope(slope)
, _type(type)
, _edges(edges)
{
}
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags();
}
void Serialise(DataSerialiser & stream) override;
GameActions::Result::Ptr Query() const override;
GameActions::Result::Ptr Execute() const override;
private:
GameActions::Result::Ptr ElementInsertQuery(GameActions::Result::Ptr res) const;
GameActions::Result::Ptr ElementInsertExecute(GameActions::Result::Ptr res) const;
};

View File

@ -1,279 +0,0 @@
/*****************************************************************************
* 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
#include "../Cheats.h"
#include "../OpenRCT2.h"
#include "../core/MemoryStream.h"
#include "../interface/Window.h"
#include "../localisation/StringIds.h"
#include "../management/Finance.h"
#include "../world/Footpath.h"
#include "../world/Location.hpp"
#include "../world/Park.h"
#include "../world/Surface.h"
#include "../world/Wall.h"
#include "GameAction.h"
DEFINE_GAME_ACTION(FootpathPlaceFromTrackAction, GAME_COMMAND_PLACE_PATH_FROM_TRACK, GameActions::Result)
{
private:
CoordsXYZ _loc;
uint8_t _slope{};
ObjectEntryIndex _type{};
uint8_t _edges{};
public:
FootpathPlaceFromTrackAction() = default;
FootpathPlaceFromTrackAction(const CoordsXYZ& loc, uint8_t slope, ObjectEntryIndex type, uint8_t edges)
: _loc(loc)
, _slope(slope)
, _type(type)
, _edges(edges)
{
}
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags();
}
void Serialise(DataSerialiser & stream) override
{
GameAction::Serialise(stream);
stream << DS_TAG(_loc) << DS_TAG(_slope) << DS_TAG(_type) << DS_TAG(_edges);
}
GameActions::Result::Ptr Query() const override
{
GameActions::Result::Ptr res = std::make_unique<GameActions::Result>();
res->Cost = 0;
res->Expenditure = ExpenditureType::Landscaping;
res->Position = _loc.ToTileCentre();
gFootpathGroundFlags = 0;
if (!LocationValid(_loc) || map_is_edge(_loc))
{
return MakeResult(
GameActions::Status::InvalidParameters, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, STR_OFF_EDGE_OF_MAP);
}
if (!((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode) && !map_is_location_owned(_loc))
{
return MakeResult(
GameActions::Status::Disallowed, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, STR_LAND_NOT_OWNED_BY_PARK);
}
if (_loc.z < FootpathMinHeight)
{
return MakeResult(GameActions::Status::Disallowed, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, STR_TOO_LOW);
}
if (_loc.z > FootpathMaxHeight)
{
return MakeResult(GameActions::Status::Disallowed, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, STR_TOO_HIGH);
}
return ElementInsertQuery(std::move(res));
}
GameActions::Result::Ptr Execute() const override
{
GameActions::Result::Ptr res = std::make_unique<GameActions::Result>();
res->Cost = 0;
res->Expenditure = ExpenditureType::Landscaping;
res->Position = _loc.ToTileCentre();
if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST))
{
footpath_interrupt_peeps(_loc);
}
gFootpathGroundFlags = 0;
// Force ride construction to recheck area
_currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_RECHECK;
return ElementInsertExecute(std::move(res));
}
private:
GameActions::Result::Ptr ElementInsertQuery(GameActions::Result::Ptr res) const
{
bool entrancePath = false, entranceIsSamePath = false;
if (!map_check_free_elements_and_reorganise(1))
{
return MakeResult(GameActions::Status::NoFreeElements, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE);
}
res->Cost = MONEY(12, 00);
QuarterTile quarterTile{ 0b1111, 0 };
auto zLow = _loc.z;
auto zHigh = zLow + PATH_CLEARANCE;
if (_slope & FOOTPATH_PROPERTIES_FLAG_IS_SLOPED)
{
quarterTile = QuarterTile{ 0b1111, 0b1100 }.Rotate(_slope & TILE_ELEMENT_DIRECTION_MASK);
zHigh += PATH_HEIGHT_STEP;
}
auto entranceElement = map_get_park_entrance_element_at(_loc, false);
// Make sure the entrance part is the middle
if (entranceElement != nullptr && (entranceElement->GetSequenceIndex()) == 0)
{
entrancePath = true;
// Make the price the same as replacing a path
if (entranceElement->GetPathType() == (_type & 0xF))
entranceIsSamePath = true;
else
res->Cost -= MONEY(6, 00);
}
// Do not attempt to build a crossing with a queue or a sloped.
uint8_t crossingMode = (_type & FOOTPATH_ELEMENT_INSERT_QUEUE) || (_slope != TILE_ELEMENT_SLOPE_FLAT)
? CREATE_CROSSING_MODE_NONE
: CREATE_CROSSING_MODE_PATH_OVER_TRACK;
if (!entrancePath
&& !map_can_construct_with_clear_at(
{ _loc, zLow, zHigh }, &map_place_non_scenery_clear_func, quarterTile, GetFlags(), &res->Cost, crossingMode))
{
return MakeResult(
GameActions::Status::NoClearance, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, gGameCommandErrorText,
gCommonFormatArgs);
}
gFootpathGroundFlags = gMapGroundFlags;
if (!gCheatsDisableClearanceChecks && (gMapGroundFlags & ELEMENT_IS_UNDERWATER))
{
return MakeResult(
GameActions::Status::Disallowed, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE,
STR_CANT_BUILD_THIS_UNDERWATER);
}
auto surfaceElement = map_get_surface_element_at(_loc);
if (surfaceElement == nullptr)
{
return MakeResult(GameActions::Status::InvalidParameters, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE);
}
int32_t supportHeight = zLow - surfaceElement->GetBaseZ();
res->Cost += supportHeight < 0 ? MONEY(20, 00) : (supportHeight / PATH_HEIGHT_STEP) * MONEY(5, 00);
// Prevent the place sound from being spammed
if (entranceIsSamePath)
res->Cost = 0;
return res;
}
GameActions::Result::Ptr ElementInsertExecute(GameActions::Result::Ptr res) const
{
bool entrancePath = false, entranceIsSamePath = false;
if (!(GetFlags() & (GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_GHOST)))
{
footpath_remove_litter(_loc);
}
res->Cost = MONEY(12, 00);
QuarterTile quarterTile{ 0b1111, 0 };
auto zLow = _loc.z;
auto zHigh = zLow + PATH_CLEARANCE;
if (_slope & FOOTPATH_PROPERTIES_FLAG_IS_SLOPED)
{
quarterTile = QuarterTile{ 0b1111, 0b1100 }.Rotate(_slope & TILE_ELEMENT_DIRECTION_MASK);
zHigh += PATH_HEIGHT_STEP;
}
auto entranceElement = map_get_park_entrance_element_at(_loc, false);
// Make sure the entrance part is the middle
if (entranceElement != nullptr && (entranceElement->GetSequenceIndex()) == 0)
{
entrancePath = true;
// Make the price the same as replacing a path
if (entranceElement->GetPathType() == (_type & 0xF))
entranceIsSamePath = true;
else
res->Cost -= MONEY(6, 00);
}
// Do not attempt to build a crossing with a queue or a sloped.
uint8_t crossingMode = (_type & FOOTPATH_ELEMENT_INSERT_QUEUE) || (_slope != TILE_ELEMENT_SLOPE_FLAT)
? CREATE_CROSSING_MODE_NONE
: CREATE_CROSSING_MODE_PATH_OVER_TRACK;
if (!entrancePath
&& !map_can_construct_with_clear_at(
{ _loc, zLow, zHigh }, &map_place_non_scenery_clear_func, quarterTile, GAME_COMMAND_FLAG_APPLY | GetFlags(),
&res->Cost, crossingMode))
{
return MakeResult(
GameActions::Status::NoClearance, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, gGameCommandErrorText,
gCommonFormatArgs);
}
gFootpathGroundFlags = gMapGroundFlags;
auto surfaceElement = map_get_surface_element_at(_loc);
if (surfaceElement == nullptr)
{
return MakeResult(GameActions::Status::InvalidParameters, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE);
}
int32_t supportHeight = zLow - surfaceElement->GetBaseZ();
res->Cost += supportHeight < 0 ? MONEY(20, 00) : (supportHeight / PATH_HEIGHT_STEP) * MONEY(5, 00);
if (entrancePath)
{
if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST) && !entranceIsSamePath)
{
// Set the path type but make sure it's not a queue as that will not show up
entranceElement->SetPathType(_type & 0x7F);
map_invalidate_tile_full(_loc);
}
}
else
{
auto tileElement = tile_element_insert(_loc, 0b1111);
assert(tileElement != nullptr);
tileElement->SetType(TILE_ELEMENT_TYPE_PATH);
PathElement* pathElement = tileElement->AsPath();
pathElement->SetClearanceZ(zHigh);
pathElement->SetSurfaceEntryIndex(_type & ~FOOTPATH_ELEMENT_INSERT_QUEUE);
pathElement->SetSlopeDirection(_slope & FOOTPATH_PROPERTIES_SLOPE_DIRECTION_MASK);
if (_slope & FOOTPATH_PROPERTIES_FLAG_IS_SLOPED)
{
pathElement->SetSloped(true);
}
if (_type & FOOTPATH_ELEMENT_INSERT_QUEUE)
{
pathElement->SetIsQueue(true);
}
pathElement->SetAddition(0);
pathElement->SetRideIndex(RIDE_ID_NULL);
pathElement->SetAdditionStatus(255);
pathElement->SetIsBroken(false);
pathElement->SetEdges(_edges);
pathElement->SetCorners(0);
if (GetFlags() & GAME_COMMAND_FLAG_GHOST)
{
pathElement->SetGhost(true);
}
map_invalidate_tile_full(_loc);
}
// Prevent the place sound from being spammed
if (entranceIsSamePath)
res->Cost = 0;
return res;
}
};

View File

@ -0,0 +1,174 @@
/*****************************************************************************
* 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.
*****************************************************************************/
#include "FootpathRemoveAction.h"
#include "../Cheats.h"
#include "../OpenRCT2.h"
#include "../core/MemoryStream.h"
#include "../interface/Window.h"
#include "../localisation/StringIds.h"
#include "../management/Finance.h"
#include "../world/Footpath.h"
#include "../world/Location.hpp"
#include "../world/Park.h"
#include "../world/Wall.h"
#include "BannerRemoveAction.h"
void FootpathRemoveAction::AcceptParameters(GameActionParameterVisitor& visitor)
{
visitor.Visit(_loc);
}
void FootpathRemoveAction::Serialise(DataSerialiser& stream)
{
GameAction::Serialise(stream);
stream << DS_TAG(_loc);
}
GameActions::Result::Ptr FootpathRemoveAction::Query() const
{
GameActions::Result::Ptr res = std::make_unique<GameActions::Result>();
res->Cost = 0;
res->Expenditure = ExpenditureType::Landscaping;
res->Position = { _loc.x + 16, _loc.y + 16, _loc.z };
if (!LocationValid(_loc))
{
return MakeResult(GameActions::Status::NotOwned, STR_CANT_REMOVE_FOOTPATH_FROM_HERE, STR_LAND_NOT_OWNED_BY_PARK);
}
if (!((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode) && !map_is_location_owned(_loc))
{
return MakeResult(GameActions::Status::NotOwned, STR_CANT_REMOVE_FOOTPATH_FROM_HERE, STR_LAND_NOT_OWNED_BY_PARK);
}
TileElement* footpathElement = GetFootpathElement();
if (footpathElement == nullptr)
{
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REMOVE_FOOTPATH_FROM_HERE);
}
res->Cost = GetRefundPrice(footpathElement);
return res;
}
GameActions::Result::Ptr FootpathRemoveAction::Execute() const
{
GameActions::Result::Ptr res = std::make_unique<GameActions::Result>();
res->Cost = 0;
res->Expenditure = ExpenditureType::Landscaping;
res->Position = { _loc.x + 16, _loc.y + 16, _loc.z };
if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST))
{
footpath_interrupt_peeps(_loc);
footpath_remove_litter(_loc);
}
TileElement* footpathElement = GetFootpathElement();
if (footpathElement != nullptr)
{
footpath_queue_chain_reset();
auto bannerRes = RemoveBannersAtElement(_loc, footpathElement);
if (bannerRes->Error == GameActions::Status::Ok)
{
res->Cost += bannerRes->Cost;
}
footpath_remove_edges_at(_loc, footpathElement);
map_invalidate_tile_full(_loc);
tile_element_remove(footpathElement);
footpath_update_queue_chains();
// Remove the spawn point (if there is one in the current tile)
gPeepSpawns.erase(
std::remove_if(
gPeepSpawns.begin(), gPeepSpawns.end(),
[this](const CoordsXYZ& spawn) {
{
return spawn.ToTileStart() == _loc.ToTileStart();
}
}),
gPeepSpawns.end());
}
else
{
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REMOVE_FOOTPATH_FROM_HERE);
}
res->Cost += GetRefundPrice(footpathElement);
return res;
}
TileElement* FootpathRemoveAction::GetFootpathElement() const
{
bool getGhostPath = GetFlags() & GAME_COMMAND_FLAG_GHOST;
TileElement* tileElement = map_get_footpath_element(_loc);
TileElement* footpathElement = nullptr;
if (tileElement != nullptr)
{
if (getGhostPath && !tileElement->IsGhost())
{
while (!(tileElement++)->IsLastForTile())
{
if (tileElement->GetType() != TILE_ELEMENT_TYPE_PATH && !tileElement->IsGhost())
{
continue;
}
footpathElement = tileElement;
break;
}
}
else
{
footpathElement = tileElement;
}
}
return footpathElement;
}
money32 FootpathRemoveAction::GetRefundPrice(TileElement* footpathElement) const
{
money32 cost = -MONEY(10, 00);
return cost;
}
/**
*
* rct2: 0x006BA23E
*/
GameActions::Result::Ptr FootpathRemoveAction::RemoveBannersAtElement(const CoordsXY& loc, TileElement* tileElement) const
{
auto result = MakeResult();
while (!(tileElement++)->IsLastForTile())
{
if (tileElement->GetType() == TILE_ELEMENT_TYPE_PATH)
return result;
else if (tileElement->GetType() != TILE_ELEMENT_TYPE_BANNER)
continue;
auto bannerRemoveAction = BannerRemoveAction({ loc, tileElement->GetBaseZ(), tileElement->AsBanner()->GetPosition() });
bool isGhost = tileElement->IsGhost();
auto bannerFlags = GetFlags() | (isGhost ? static_cast<uint32_t>(GAME_COMMAND_FLAG_GHOST) : 0);
bannerRemoveAction.SetFlags(bannerFlags);
auto res = GameActions::ExecuteNested(&bannerRemoveAction);
// Ghost removal is free
if (res->Error == GameActions::Status::Ok && !isGhost)
{
result->Cost += res->Cost;
}
tileElement--;
}
return result;
}

View File

@ -0,0 +1,42 @@
/*****************************************************************************
* 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
#include "../management/Finance.h"
#include "GameAction.h"
DEFINE_GAME_ACTION(FootpathRemoveAction, GAME_COMMAND_REMOVE_PATH, GameActions::Result)
{
private:
CoordsXYZ _loc;
public:
FootpathRemoveAction() = default;
FootpathRemoveAction(const CoordsXYZ& location)
: _loc(location)
{
}
void AcceptParameters(GameActionParameterVisitor & visitor) override;
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags();
}
void Serialise(DataSerialiser & stream) override;
GameActions::Result::Ptr Query() const override;
GameActions::Result::Ptr Execute() const override;
private:
TileElement* GetFootpathElement() const;
money32 GetRefundPrice(TileElement * footpathElement) const;
GameActions::Result::Ptr RemoveBannersAtElement(const CoordsXY& loc, TileElement* tileElement) const;
};

View File

@ -1,195 +0,0 @@
/*****************************************************************************
* 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
#include "../Cheats.h"
#include "../OpenRCT2.h"
#include "../core/MemoryStream.h"
#include "../interface/Window.h"
#include "../localisation/StringIds.h"
#include "../management/Finance.h"
#include "../world/Footpath.h"
#include "../world/Location.hpp"
#include "../world/Park.h"
#include "../world/Wall.h"
#include "BannerRemoveAction.hpp"
#include "GameAction.h"
DEFINE_GAME_ACTION(FootpathRemoveAction, GAME_COMMAND_REMOVE_PATH, GameActions::Result)
{
private:
CoordsXYZ _loc;
public:
FootpathRemoveAction() = default;
FootpathRemoveAction(const CoordsXYZ& location)
: _loc(location)
{
}
void AcceptParameters(GameActionParameterVisitor & visitor) override
{
visitor.Visit(_loc);
}
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags();
}
void Serialise(DataSerialiser & stream) override
{
GameAction::Serialise(stream);
stream << DS_TAG(_loc);
}
GameActions::Result::Ptr Query() const override
{
GameActions::Result::Ptr res = std::make_unique<GameActions::Result>();
res->Cost = 0;
res->Expenditure = ExpenditureType::Landscaping;
res->Position = { _loc.x + 16, _loc.y + 16, _loc.z };
if (!LocationValid(_loc))
{
return MakeResult(GameActions::Status::NotOwned, STR_CANT_REMOVE_FOOTPATH_FROM_HERE, STR_LAND_NOT_OWNED_BY_PARK);
}
if (!((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode) && !map_is_location_owned(_loc))
{
return MakeResult(GameActions::Status::NotOwned, STR_CANT_REMOVE_FOOTPATH_FROM_HERE, STR_LAND_NOT_OWNED_BY_PARK);
}
TileElement* footpathElement = GetFootpathElement();
if (footpathElement == nullptr)
{
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REMOVE_FOOTPATH_FROM_HERE);
}
res->Cost = GetRefundPrice(footpathElement);
return res;
}
GameActions::Result::Ptr Execute() const override
{
GameActions::Result::Ptr res = std::make_unique<GameActions::Result>();
res->Cost = 0;
res->Expenditure = ExpenditureType::Landscaping;
res->Position = { _loc.x + 16, _loc.y + 16, _loc.z };
if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST))
{
footpath_interrupt_peeps(_loc);
footpath_remove_litter(_loc);
}
TileElement* footpathElement = GetFootpathElement();
if (footpathElement != nullptr)
{
footpath_queue_chain_reset();
auto bannerRes = RemoveBannersAtElement(_loc, footpathElement);
if (bannerRes->Error == GameActions::Status::Ok)
{
res->Cost += bannerRes->Cost;
}
footpath_remove_edges_at(_loc, footpathElement);
map_invalidate_tile_full(_loc);
tile_element_remove(footpathElement);
footpath_update_queue_chains();
// Remove the spawn point (if there is one in the current tile)
gPeepSpawns.erase(
std::remove_if(
gPeepSpawns.begin(), gPeepSpawns.end(),
[this](const CoordsXYZ& spawn) {
{
return spawn.ToTileStart() == _loc.ToTileStart();
}
}),
gPeepSpawns.end());
}
else
{
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_REMOVE_FOOTPATH_FROM_HERE);
}
res->Cost += GetRefundPrice(footpathElement);
return res;
}
private:
TileElement* GetFootpathElement() const
{
bool getGhostPath = GetFlags() & GAME_COMMAND_FLAG_GHOST;
TileElement* tileElement = map_get_footpath_element(_loc);
TileElement* footpathElement = nullptr;
if (tileElement != nullptr)
{
if (getGhostPath && !tileElement->IsGhost())
{
while (!(tileElement++)->IsLastForTile())
{
if (tileElement->GetType() != TILE_ELEMENT_TYPE_PATH && !tileElement->IsGhost())
{
continue;
}
footpathElement = tileElement;
break;
}
}
else
{
footpathElement = tileElement;
}
}
return footpathElement;
}
money32 GetRefundPrice(TileElement * footpathElement) const
{
money32 cost = -MONEY(10, 00);
return cost;
}
/**
*
* rct2: 0x006BA23E
*/
GameActions::Result::Ptr RemoveBannersAtElement(const CoordsXY& loc, TileElement* tileElement) const
{
auto result = MakeResult();
while (!(tileElement++)->IsLastForTile())
{
if (tileElement->GetType() == TILE_ELEMENT_TYPE_PATH)
return result;
else if (tileElement->GetType() != TILE_ELEMENT_TYPE_BANNER)
continue;
auto bannerRemoveAction = BannerRemoveAction(
{ loc, tileElement->GetBaseZ(), tileElement->AsBanner()->GetPosition() });
bool isGhost = tileElement->IsGhost();
auto bannerFlags = GetFlags() | (isGhost ? static_cast<uint32_t>(GAME_COMMAND_FLAG_GHOST) : 0);
bannerRemoveAction.SetFlags(bannerFlags);
auto res = GameActions::ExecuteNested(&bannerRemoveAction);
// Ghost removal is free
if (res->Error == GameActions::Status::Ok && !isGhost)
{
result->Cost += res->Cost;
}
tileElement--;
}
return result;
}
};

View File

@ -7,18 +7,22 @@
* OpenRCT2 is licensed under the GNU General Public License version 3.
*****************************************************************************/
#include "../peep/Staff.h"
#include "../ride/Track.h"
#include "../world/Entrance.h"
#include "../world/Park.h"
#include "GameAction.h"
#include "GuestSetNameAction.hpp"
#include "MazeSetTrackAction.hpp"
#include "PlaceParkEntranceAction.hpp"
#include "PlacePeepSpawnAction.hpp"
#include "RideCreateAction.hpp"
#include "RideDemolishAction.hpp"
#include "RideSetName.hpp"
#include "RideSetStatus.hpp"
#include "SetParkEntranceFeeAction.hpp"
#include "StaffSetNameAction.hpp"
#include "WallRemoveAction.hpp"
#include "GuestSetNameAction.h"
#include "MazeSetTrackAction.h"
#include "PlaceParkEntranceAction.h"
#include "PlacePeepSpawnAction.h"
#include "RideCreateAction.h"
#include "RideDemolishAction.h"
#include "RideSetNameAction.h"
#include "RideSetStatusAction.h"
#include "SetParkEntranceFeeAction.h"
#include "StaffSetNameAction.h"
#include "WallRemoveAction.h"
#pragma region PlaceParkEntranceAction
/**

View File

@ -7,87 +7,87 @@
* OpenRCT2 is licensed under the GNU General Public License version 3.
*****************************************************************************/
#include "BalloonPressAction.hpp"
#include "BannerPlaceAction.hpp"
#include "BannerRemoveAction.hpp"
#include "BannerSetColourAction.hpp"
#include "BannerSetNameAction.hpp"
#include "BannerSetStyleAction.hpp"
#include "ClearAction.hpp"
#include "ClimateSetAction.hpp"
#include "CustomAction.hpp"
#include "FootpathAdditionPlaceAction.hpp"
#include "FootpathAdditionRemoveAction.hpp"
#include "FootpathPlaceAction.hpp"
#include "FootpathPlaceFromTrackAction.hpp"
#include "FootpathRemoveAction.hpp"
#include "BalloonPressAction.h"
#include "BannerPlaceAction.h"
#include "BannerRemoveAction.h"
#include "BannerSetColourAction.h"
#include "BannerSetNameAction.h"
#include "BannerSetStyleAction.h"
#include "ClearAction.h"
#include "ClimateSetAction.h"
#include "CustomAction.h"
#include "FootpathAdditionPlaceAction.h"
#include "FootpathAdditionRemoveAction.h"
#include "FootpathPlaceAction.h"
#include "FootpathPlaceFromTrackAction.h"
#include "FootpathRemoveAction.h"
#include "GameAction.h"
#include "GuestSetFlagsAction.hpp"
#include "GuestSetNameAction.hpp"
#include "LandBuyRightsAction.hpp"
#include "LandLowerAction.hpp"
#include "LandRaiseAction.hpp"
#include "LandSetHeightAction.hpp"
#include "LandSetRightsAction.hpp"
#include "LandSmoothAction.hpp"
#include "LargeSceneryPlaceAction.hpp"
#include "LargeSceneryRemoveAction.hpp"
#include "LargeScenerySetColourAction.hpp"
#include "LoadOrQuitAction.hpp"
#include "MazePlaceTrackAction.hpp"
#include "MazeSetTrackAction.hpp"
#include "NetworkModifyGroupAction.hpp"
#include "ParkEntranceRemoveAction.hpp"
#include "ParkMarketingAction.hpp"
#include "ParkSetDateAction.hpp"
#include "ParkSetLoanAction.hpp"
#include "ParkSetNameAction.hpp"
#include "ParkSetParameterAction.hpp"
#include "ParkSetResearchFundingAction.hpp"
#include "PauseToggleAction.hpp"
#include "PeepPickupAction.hpp"
#include "PlaceParkEntranceAction.hpp"
#include "PlacePeepSpawnAction.hpp"
#include "PlayerKickAction.hpp"
#include "PlayerSetGroupAction.hpp"
#include "RideCreateAction.hpp"
#include "RideDemolishAction.hpp"
#include "RideEntranceExitPlaceAction.hpp"
#include "RideEntranceExitRemoveAction.hpp"
#include "RideSetAppearanceAction.hpp"
#include "RideSetColourScheme.hpp"
#include "RideSetName.hpp"
#include "RideSetPriceAction.hpp"
#include "RideSetSetting.hpp"
#include "RideSetStatus.hpp"
#include "RideSetVehiclesAction.hpp"
#include "ScenarioSetSettingAction.hpp"
#include "SetCheatAction.hpp"
#include "SetParkEntranceFeeAction.hpp"
#include "SignSetNameAction.hpp"
#include "SignSetStyleAction.hpp"
#include "SmallSceneryPlaceAction.hpp"
#include "SmallSceneryRemoveAction.hpp"
#include "SmallScenerySetColourAction.hpp"
#include "StaffFireAction.hpp"
#include "StaffHireNewAction.hpp"
#include "StaffSetColourAction.hpp"
#include "StaffSetCostumeAction.hpp"
#include "StaffSetNameAction.hpp"
#include "StaffSetOrdersAction.hpp"
#include "StaffSetPatrolAreaAction.hpp"
#include "SurfaceSetStyleAction.hpp"
#include "TileModifyAction.hpp"
#include "GuestSetFlagsAction.h"
#include "GuestSetNameAction.h"
#include "LandBuyRightsAction.h"
#include "LandLowerAction.h"
#include "LandRaiseAction.h"
#include "LandSetHeightAction.h"
#include "LandSetRightsAction.h"
#include "LandSmoothAction.h"
#include "LargeSceneryPlaceAction.h"
#include "LargeSceneryRemoveAction.h"
#include "LargeScenerySetColourAction.h"
#include "LoadOrQuitAction.h"
#include "MazePlaceTrackAction.h"
#include "MazeSetTrackAction.h"
#include "NetworkModifyGroupAction.h"
#include "ParkEntranceRemoveAction.h"
#include "ParkMarketingAction.h"
#include "ParkSetDateAction.h"
#include "ParkSetLoanAction.h"
#include "ParkSetNameAction.h"
#include "ParkSetParameterAction.h"
#include "ParkSetResearchFundingAction.h"
#include "PauseToggleAction.h"
#include "PeepPickupAction.h"
#include "PlaceParkEntranceAction.h"
#include "PlacePeepSpawnAction.h"
#include "PlayerKickAction.h"
#include "PlayerSetGroupAction.h"
#include "RideCreateAction.h"
#include "RideDemolishAction.h"
#include "RideEntranceExitPlaceAction.h"
#include "RideEntranceExitRemoveAction.h"
#include "RideSetAppearanceAction.h"
#include "RideSetColourSchemeAction.h"
#include "RideSetNameAction.h"
#include "RideSetPriceAction.h"
#include "RideSetSettingAction.h"
#include "RideSetStatusAction.h"
#include "RideSetVehicleAction.h"
#include "ScenarioSetSettingAction.h"
#include "SetCheatAction.h"
#include "SetParkEntranceFeeAction.h"
#include "SignSetNameAction.h"
#include "SignSetStyleAction.h"
#include "SmallSceneryPlaceAction.h"
#include "SmallSceneryRemoveAction.h"
#include "SmallScenerySetColourAction.h"
#include "StaffFireAction.h"
#include "StaffHireNewAction.h"
#include "StaffSetColourAction.h"
#include "StaffSetCostumeAction.h"
#include "StaffSetNameAction.h"
#include "StaffSetOrdersAction.h"
#include "StaffSetPatrolAreaAction.h"
#include "SurfaceSetStyleAction.h"
#include "TileModifyAction.h"
#include "TrackDesignAction.h"
#include "TrackPlaceAction.hpp"
#include "TrackRemoveAction.hpp"
#include "TrackSetBrakeSpeedAction.hpp"
#include "WallPlaceAction.hpp"
#include "WallRemoveAction.hpp"
#include "WallSetColourAction.hpp"
#include "WaterLowerAction.hpp"
#include "WaterRaiseAction.hpp"
#include "WaterSetHeightAction.hpp"
#include "TrackPlaceAction.h"
#include "TrackRemoveAction.h"
#include "TrackSetBrakeSpeedAction.h"
#include "WallPlaceAction.h"
#include "WallRemoveAction.h"
#include "WallSetColourAction.h"
#include "WaterLowerAction.h"
#include "WaterRaiseAction.h"
#include "WaterSetHeightAction.h"
namespace GameActions
{

View File

@ -0,0 +1,51 @@
/*****************************************************************************
* 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.
*****************************************************************************/
#include "GuestSetFlagsAction.h"
#include "../Context.h"
#include "../OpenRCT2.h"
void GuestSetFlagsAction::AcceptParameters(GameActionParameterVisitor& visitor)
{
visitor.Visit("peep", _peepId);
visitor.Visit("flags", _newFlags);
}
void GuestSetFlagsAction::Serialise(DataSerialiser& stream)
{
GameAction::Serialise(stream);
stream << DS_TAG(_peepId) << DS_TAG(_newFlags);
}
GameActions::Result::Ptr GuestSetFlagsAction::Query() const
{
Peep* peep = TryGetEntity<Peep>(_peepId);
if (peep == nullptr)
{
log_error("Used invalid sprite index for peep: %u", static_cast<uint32_t>(_peepId));
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_CHANGE_THIS);
}
return std::make_unique<GameActions::Result>();
}
GameActions::Result::Ptr GuestSetFlagsAction::Execute() const
{
Peep* peep = TryGetEntity<Peep>(_peepId);
if (peep == nullptr)
{
log_error("Used invalid sprite index for peep: %u", static_cast<uint32_t>(_peepId));
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_CHANGE_THIS);
}
peep->PeepFlags = _newFlags;
return std::make_unique<GameActions::Result>();
}

View File

@ -0,0 +1,39 @@
/*****************************************************************************
* 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
#include "../world/Sprite.h"
#include "GameAction.h"
DEFINE_GAME_ACTION(GuestSetFlagsAction, GAME_COMMAND_GUEST_SET_FLAGS, GameActions::Result)
{
private:
uint16_t _peepId{ SPRITE_INDEX_NULL };
uint32_t _newFlags{};
public:
GuestSetFlagsAction() = default;
GuestSetFlagsAction(uint16_t peepId, uint32_t flags)
: _peepId(peepId)
, _newFlags(flags)
{
}
void AcceptParameters(GameActionParameterVisitor & visitor) override;
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags() | GameActions::Flags::AllowWhilePaused;
}
void Serialise(DataSerialiser & stream) override;
GameActions::Result::Ptr Query() const override;
GameActions::Result::Ptr Execute() const override;
};

View File

@ -1,73 +0,0 @@
/*****************************************************************************
* 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
#include "../Context.h"
#include "../OpenRCT2.h"
#include "../world/Sprite.h"
#include "GameAction.h"
DEFINE_GAME_ACTION(GuestSetFlagsAction, GAME_COMMAND_GUEST_SET_FLAGS, GameActions::Result)
{
private:
uint16_t _peepId{ SPRITE_INDEX_NULL };
uint32_t _newFlags{};
public:
GuestSetFlagsAction() = default;
GuestSetFlagsAction(uint16_t peepId, uint32_t flags)
: _peepId(peepId)
, _newFlags(flags)
{
}
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags() | GameActions::Flags::AllowWhilePaused;
}
void AcceptParameters(GameActionParameterVisitor & visitor) override
{
visitor.Visit("peep", _peepId);
visitor.Visit("flags", _newFlags);
}
void Serialise(DataSerialiser & stream) override
{
GameAction::Serialise(stream);
stream << DS_TAG(_peepId) << DS_TAG(_newFlags);
}
GameActions::Result::Ptr Query() const override
{
Peep* peep = TryGetEntity<Peep>(_peepId);
if (peep == nullptr)
{
log_error("Used invalid sprite index for peep: %u", static_cast<uint32_t>(_peepId));
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_CHANGE_THIS);
}
return std::make_unique<GameActions::Result>();
}
GameActions::Result::Ptr Execute() const override
{
Peep* peep = TryGetEntity<Peep>(_peepId);
if (peep == nullptr)
{
log_error("Used invalid sprite index for peep: %u", static_cast<uint32_t>(_peepId));
return MakeResult(GameActions::Status::InvalidParameters, STR_CANT_CHANGE_THIS);
}
peep->PeepFlags = _newFlags;
return std::make_unique<GameActions::Result>();
}
};

View File

@ -0,0 +1,86 @@
/*****************************************************************************
* 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.
*****************************************************************************/
#include "GuestSetNameAction.h"
#include "../Cheats.h"
#include "../Context.h"
#include "../core/MemoryStream.h"
#include "../drawing/Drawing.h"
#include "../interface/Window.h"
#include "../localisation/Localisation.h"
#include "../localisation/StringIds.h"
#include "../windows/Intent.h"
#include "../world/Park.h"
#include "../world/Sprite.h"
void GuestSetNameAction::AcceptParameters(GameActionParameterVisitor& visitor)
{
visitor.Visit("peep", _spriteIndex);
visitor.Visit("name", _name);
}
void GuestSetNameAction::Serialise(DataSerialiser& stream)
{
GameAction::Serialise(stream);
stream << DS_TAG(_spriteIndex) << DS_TAG(_name);
}
GameActions::Result::Ptr GuestSetNameAction::Query() const
{
if (_spriteIndex >= MAX_SPRITES)
{
return std::make_unique<GameActions::Result>(GameActions::Status::InvalidParameters, STR_CANT_NAME_GUEST, STR_NONE);
}
auto guest = TryGetEntity<Guest>(_spriteIndex);
if (guest == nullptr)
{
log_warning("Invalid game command for sprite %u", _spriteIndex);
return std::make_unique<GameActions::Result>(GameActions::Status::InvalidParameters, STR_CANT_NAME_GUEST, STR_NONE);
}
return std::make_unique<GameActions::Result>();
}
GameActions::Result::Ptr GuestSetNameAction::Execute() const
{
auto guest = TryGetEntity<Guest>(_spriteIndex);
if (guest == nullptr)
{
log_warning("Invalid game command for sprite %u", _spriteIndex);
return std::make_unique<GameActions::Result>(GameActions::Status::InvalidParameters, STR_CANT_NAME_GUEST, STR_NONE);
}
auto curName = guest->GetName();
if (curName == _name)
{
return std::make_unique<GameActions::Result>(GameActions::Status::Ok, STR_NONE);
}
if (!guest->SetName(_name))
{
return std::make_unique<GameActions::Result>(GameActions::Status::Unknown, STR_CANT_NAME_GUEST, STR_NONE);
}
// Easter egg functions are for guests only
guest->HandleEasterEggName();
gfx_invalidate_screen();
auto intent = Intent(INTENT_ACTION_REFRESH_GUEST_LIST);
context_broadcast_intent(&intent);
auto res = std::make_unique<GameActions::Result>();
res->Position.x = guest->x;
res->Position.y = guest->y;
res->Position.z = guest->z;
return res;
}

View File

@ -0,0 +1,48 @@
/*****************************************************************************
* 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
#include "../world/Sprite.h"
#include "GameAction.h"
DEFINE_GAME_ACTION(GuestSetNameAction, GAME_COMMAND_SET_GUEST_NAME, GameActions::Result)
{
private:
uint16_t _spriteIndex{ SPRITE_INDEX_NULL };
std::string _name;
public:
GuestSetNameAction() = default;
GuestSetNameAction(uint16_t spriteIndex, const std::string& name)
: _spriteIndex(spriteIndex)
, _name(name)
{
}
uint16_t GetSpriteIndex() const
{
return _spriteIndex;
}
std::string GetGuestName() const
{
return _name;
}
void AcceptParameters(GameActionParameterVisitor & visitor) override;
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags() | GameActions::Flags::AllowWhilePaused;
}
void Serialise(DataSerialiser & stream) override;
GameActions::Result::Ptr Query() const override;
GameActions::Result::Ptr Execute() const override;
};

View File

@ -1,117 +0,0 @@
/*****************************************************************************
* 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
#include "../Cheats.h"
#include "../Context.h"
#include "../core/MemoryStream.h"
#include "../drawing/Drawing.h"
#include "../interface/Window.h"
#include "../localisation/Localisation.h"
#include "../localisation/StringIds.h"
#include "../windows/Intent.h"
#include "../world/Park.h"
#include "../world/Sprite.h"
#include "GameAction.h"
DEFINE_GAME_ACTION(GuestSetNameAction, GAME_COMMAND_SET_GUEST_NAME, GameActions::Result)
{
private:
uint16_t _spriteIndex{ SPRITE_INDEX_NULL };
std::string _name;
public:
GuestSetNameAction() = default;
GuestSetNameAction(uint16_t spriteIndex, const std::string& name)
: _spriteIndex(spriteIndex)
, _name(name)
{
}
void AcceptParameters(GameActionParameterVisitor & visitor) override
{
visitor.Visit("peep", _spriteIndex);
visitor.Visit("name", _name);
}
uint16_t GetSpriteIndex() const
{
return _spriteIndex;
}
std::string GetGuestName() const
{
return _name;
}
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags() | GameActions::Flags::AllowWhilePaused;
}
void Serialise(DataSerialiser & stream) override
{
GameAction::Serialise(stream);
stream << DS_TAG(_spriteIndex) << DS_TAG(_name);
}
GameActions::Result::Ptr Query() const override
{
if (_spriteIndex >= MAX_SPRITES)
{
return std::make_unique<GameActions::Result>(GameActions::Status::InvalidParameters, STR_CANT_NAME_GUEST, STR_NONE);
}
auto guest = TryGetEntity<Guest>(_spriteIndex);
if (guest == nullptr)
{
log_warning("Invalid game command for sprite %u", _spriteIndex);
return std::make_unique<GameActions::Result>(GameActions::Status::InvalidParameters, STR_CANT_NAME_GUEST, STR_NONE);
}
return std::make_unique<GameActions::Result>();
}
GameActions::Result::Ptr Execute() const override
{
auto guest = TryGetEntity<Guest>(_spriteIndex);
if (guest == nullptr)
{
log_warning("Invalid game command for sprite %u", _spriteIndex);
return std::make_unique<GameActions::Result>(GameActions::Status::InvalidParameters, STR_CANT_NAME_GUEST, STR_NONE);
}
auto curName = guest->GetName();
if (curName == _name)
{
return std::make_unique<GameActions::Result>(GameActions::Status::Ok, STR_NONE);
}
if (!guest->SetName(_name))
{
return std::make_unique<GameActions::Result>(GameActions::Status::Unknown, STR_CANT_NAME_GUEST, STR_NONE);
}
// Easter egg functions are for guests only
guest->HandleEasterEggName();
gfx_invalidate_screen();
auto intent = Intent(INTENT_ACTION_REFRESH_GUEST_LIST);
context_broadcast_intent(&intent);
auto res = std::make_unique<GameActions::Result>();
res->Position.x = guest->x;
res->Position.y = guest->y;
res->Position.z = guest->z;
return res;
}
};

View File

@ -0,0 +1,149 @@
/*****************************************************************************
* 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.
*****************************************************************************/
#include "LandBuyRightsAction.h"
#include "../Context.h"
#include "../OpenRCT2.h"
#include "../actions/LandSetHeightAction.h"
#include "../audio/audio.h"
#include "../interface/Window.h"
#include "../localisation/Localisation.h"
#include "../localisation/StringIds.h"
#include "../management/Finance.h"
#include "../ride/RideData.h"
#include "../util/Util.h"
#include "../windows/Intent.h"
#include "../world/Park.h"
#include "../world/Scenery.h"
#include "../world/Sprite.h"
#include "../world/Surface.h"
void LandBuyRightsAction::Serialise(DataSerialiser& stream)
{
GameAction::Serialise(stream);
stream << DS_TAG(_range) << DS_TAG(_setting);
}
GameActions::Result::Ptr LandBuyRightsAction::Query() const
{
return QueryExecute(false);
}
GameActions::Result::Ptr LandBuyRightsAction::Execute() const
{
return QueryExecute(true);
}
GameActions::Result::Ptr LandBuyRightsAction::QueryExecute(bool isExecuting) const
{
auto res = MakeResult();
MapRange normRange = _range.Normalise();
// Keep big coordinates within map boundaries
auto aX = std::max<decltype(normRange.GetLeft())>(32, normRange.GetLeft());
auto bX = std::min<decltype(normRange.GetRight())>(gMapSizeMaxXY, normRange.GetRight());
auto aY = std::max<decltype(normRange.GetTop())>(32, normRange.GetTop());
auto bY = std::min<decltype(normRange.GetBottom())>(gMapSizeMaxXY, normRange.GetBottom());
MapRange validRange = MapRange{ aX, aY, bX, bY };
CoordsXYZ centre{ (validRange.GetLeft() + validRange.GetRight()) / 2 + 16,
(validRange.GetTop() + validRange.GetBottom()) / 2 + 16, 0 };
centre.z = tile_element_height(centre);
res->Position = centre;
res->Expenditure = ExpenditureType::LandPurchase;
// Game command modified to accept selection size
for (auto y = validRange.GetTop(); y <= validRange.GetBottom(); y += COORDS_XY_STEP)
{
for (auto x = validRange.GetLeft(); x <= validRange.GetRight(); x += COORDS_XY_STEP)
{
if (!LocationValid({ x, y }))
continue;
auto result = map_buy_land_rights_for_tile({ x, y }, isExecuting);
if (result->Error == GameActions::Status::Ok)
{
res->Cost += result->Cost;
}
}
}
if (isExecuting)
{
map_count_remaining_land_rights();
}
return res;
}
GameActions::Result::Ptr LandBuyRightsAction::map_buy_land_rights_for_tile(const CoordsXY& loc, bool isExecuting) const
{
if (_setting >= LandBuyRightSetting::Count)
{
log_warning("Tried calling buy land rights with an incorrect setting. setting = %u", _setting);
return MakeResult(GameActions::Status::InvalidParameters, _ErrorTitles[0], STR_NONE);
}
SurfaceElement* surfaceElement = map_get_surface_element_at(loc);
if (surfaceElement == nullptr)
{
log_error("Could not find surface. x = %d, y = %d", loc.x, loc.y);
return MakeResult(GameActions::Status::InvalidParameters, _ErrorTitles[EnumValue(_setting)], STR_NONE);
}
auto res = MakeResult();
switch (_setting)
{
case LandBuyRightSetting::BuyLand: // 0
if ((surfaceElement->GetOwnership() & OWNERSHIP_OWNED) != 0)
{ // If the land is already owned
return res;
}
if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) != 0
|| (surfaceElement->GetOwnership() & OWNERSHIP_AVAILABLE) == 0)
{
return MakeResult(GameActions::Status::NotOwned, _ErrorTitles[EnumValue(_setting)], STR_LAND_NOT_FOR_SALE);
}
if (isExecuting)
{
surfaceElement->SetOwnership(OWNERSHIP_OWNED);
update_park_fences_around_tile(loc);
}
res->Cost = gLandPrice;
return res;
case LandBuyRightSetting::BuyConstructionRights: // 2
if ((surfaceElement->GetOwnership() & (OWNERSHIP_OWNED | OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED)) != 0)
{ // If the land or construction rights are already owned
return res;
}
if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) != 0
|| (surfaceElement->GetOwnership() & OWNERSHIP_CONSTRUCTION_RIGHTS_AVAILABLE) == 0)
{
return MakeResult(
GameActions::Status::NotOwned, _ErrorTitles[EnumValue(_setting)], STR_CONSTRUCTION_RIGHTS_NOT_FOR_SALE);
}
if (isExecuting)
{
surfaceElement->SetOwnership(surfaceElement->GetOwnership() | OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED);
uint16_t baseZ = surfaceElement->GetBaseZ();
map_invalidate_tile({ loc, baseZ, baseZ + 16 });
}
res->Cost = gConstructionRightsPrice;
return res;
default:
log_warning("Tried calling buy land rights with an incorrect setting. setting = %u", _setting);
return MakeResult(GameActions::Status::InvalidParameters, _ErrorTitles[0], STR_NONE);
}
}

View File

@ -0,0 +1,56 @@
/*****************************************************************************
* 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
#include "GameAction.h"
enum class LandBuyRightSetting : uint8_t
{
BuyLand,
BuyConstructionRights,
Count
};
DEFINE_GAME_ACTION(LandBuyRightsAction, GAME_COMMAND_BUY_LAND_RIGHTS, GameActions::Result)
{
private:
MapRange _range;
LandBuyRightSetting _setting{ LandBuyRightSetting::Count };
constexpr static rct_string_id _ErrorTitles[] = { STR_CANT_BUY_LAND, STR_CANT_BUY_CONSTRUCTION_RIGHTS_HERE };
public:
LandBuyRightsAction() = default;
LandBuyRightsAction(const MapRange& range, LandBuyRightSetting setting)
: _range(range)
, _setting(setting)
{
}
LandBuyRightsAction(const CoordsXY& coord, LandBuyRightSetting setting)
: _range(coord.x, coord.y, coord.x, coord.y)
, _setting(setting)
{
}
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags();
}
void Serialise(DataSerialiser & stream) override;
GameActions::Result::Ptr Query() const override;
GameActions::Result::Ptr Execute() const override;
private:
GameActions::Result::Ptr QueryExecute(bool isExecuting) const;
GameActions::Result::Ptr map_buy_land_rights_for_tile(const CoordsXY& loc, bool isExecuting) const;
};

View File

@ -1,187 +0,0 @@
/*****************************************************************************
* 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
#include "../Context.h"
#include "../OpenRCT2.h"
#include "../actions/LandSetHeightAction.hpp"
#include "../audio/audio.h"
#include "../interface/Window.h"
#include "../localisation/Localisation.h"
#include "../localisation/StringIds.h"
#include "../management/Finance.h"
#include "../ride/RideData.h"
#include "../util/Util.h"
#include "../windows/Intent.h"
#include "../world/Park.h"
#include "../world/Scenery.h"
#include "../world/Sprite.h"
#include "../world/Surface.h"
#include "GameAction.h"
enum class LandBuyRightSetting : uint8_t
{
BuyLand,
BuyConstructionRights,
Count
};
DEFINE_GAME_ACTION(LandBuyRightsAction, GAME_COMMAND_BUY_LAND_RIGHTS, GameActions::Result)
{
private:
MapRange _range;
LandBuyRightSetting _setting{ LandBuyRightSetting::Count };
constexpr static rct_string_id _ErrorTitles[] = { STR_CANT_BUY_LAND, STR_CANT_BUY_CONSTRUCTION_RIGHTS_HERE };
public:
LandBuyRightsAction() = default;
LandBuyRightsAction(const MapRange& range, LandBuyRightSetting setting)
: _range(range)
, _setting(setting)
{
}
LandBuyRightsAction(const CoordsXY& coord, LandBuyRightSetting setting)
: _range(coord.x, coord.y, coord.x, coord.y)
, _setting(setting)
{
}
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags();
}
void Serialise(DataSerialiser & stream) override
{
GameAction::Serialise(stream);
stream << DS_TAG(_range) << DS_TAG(_setting);
}
GameActions::Result::Ptr Query() const override
{
return QueryExecute(false);
}
GameActions::Result::Ptr Execute() const override
{
return QueryExecute(true);
}
private:
GameActions::Result::Ptr QueryExecute(bool isExecuting) const
{
auto res = MakeResult();
MapRange normRange = _range.Normalise();
// Keep big coordinates within map boundaries
auto aX = std::max<decltype(normRange.GetLeft())>(32, normRange.GetLeft());
auto bX = std::min<decltype(normRange.GetRight())>(gMapSizeMaxXY, normRange.GetRight());
auto aY = std::max<decltype(normRange.GetTop())>(32, normRange.GetTop());
auto bY = std::min<decltype(normRange.GetBottom())>(gMapSizeMaxXY, normRange.GetBottom());
MapRange validRange = MapRange{ aX, aY, bX, bY };
CoordsXYZ centre{ (validRange.GetLeft() + validRange.GetRight()) / 2 + 16,
(validRange.GetTop() + validRange.GetBottom()) / 2 + 16, 0 };
centre.z = tile_element_height(centre);
res->Position = centre;
res->Expenditure = ExpenditureType::LandPurchase;
// Game command modified to accept selection size
for (auto y = validRange.GetTop(); y <= validRange.GetBottom(); y += COORDS_XY_STEP)
{
for (auto x = validRange.GetLeft(); x <= validRange.GetRight(); x += COORDS_XY_STEP)
{
if (!LocationValid({ x, y }))
continue;
auto result = map_buy_land_rights_for_tile({ x, y }, isExecuting);
if (result->Error == GameActions::Status::Ok)
{
res->Cost += result->Cost;
}
}
}
if (isExecuting)
{
map_count_remaining_land_rights();
}
return res;
}
GameActions::Result::Ptr map_buy_land_rights_for_tile(const CoordsXY& loc, bool isExecuting) const
{
if (_setting >= LandBuyRightSetting::Count)
{
log_warning("Tried calling buy land rights with an incorrect setting. setting = %u", _setting);
return MakeResult(GameActions::Status::InvalidParameters, _ErrorTitles[0], STR_NONE);
}
SurfaceElement* surfaceElement = map_get_surface_element_at(loc);
if (surfaceElement == nullptr)
{
log_error("Could not find surface. x = %d, y = %d", loc.x, loc.y);
return MakeResult(GameActions::Status::InvalidParameters, _ErrorTitles[EnumValue(_setting)], STR_NONE);
}
auto res = MakeResult();
switch (_setting)
{
case LandBuyRightSetting::BuyLand: // 0
if ((surfaceElement->GetOwnership() & OWNERSHIP_OWNED) != 0)
{ // If the land is already owned
return res;
}
if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) != 0
|| (surfaceElement->GetOwnership() & OWNERSHIP_AVAILABLE) == 0)
{
return MakeResult(GameActions::Status::NotOwned, _ErrorTitles[EnumValue(_setting)], STR_LAND_NOT_FOR_SALE);
}
if (isExecuting)
{
surfaceElement->SetOwnership(OWNERSHIP_OWNED);
update_park_fences_around_tile(loc);
}
res->Cost = gLandPrice;
return res;
case LandBuyRightSetting::BuyConstructionRights: // 2
if ((surfaceElement->GetOwnership() & (OWNERSHIP_OWNED | OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED)) != 0)
{ // If the land or construction rights are already owned
return res;
}
if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) != 0
|| (surfaceElement->GetOwnership() & OWNERSHIP_CONSTRUCTION_RIGHTS_AVAILABLE) == 0)
{
return MakeResult(
GameActions::Status::NotOwned, _ErrorTitles[EnumValue(_setting)], STR_CONSTRUCTION_RIGHTS_NOT_FOR_SALE);
}
if (isExecuting)
{
surfaceElement->SetOwnership(surfaceElement->GetOwnership() | OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED);
uint16_t baseZ = surfaceElement->GetBaseZ();
map_invalidate_tile({ loc, baseZ, baseZ + 16 });
}
res->Cost = gConstructionRightsPrice;
return res;
default:
log_warning("Tried calling buy land rights with an incorrect setting. setting = %u", _setting);
return MakeResult(GameActions::Status::InvalidParameters, _ErrorTitles[0], STR_NONE);
}
}
};

View File

@ -0,0 +1,136 @@
/*****************************************************************************
* 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.
*****************************************************************************/
#include "LandLowerAction.h"
#include "../Context.h"
#include "../OpenRCT2.h"
#include "../actions/LandSetHeightAction.h"
#include "../audio/audio.h"
#include "../interface/Window.h"
#include "../localisation/Localisation.h"
#include "../localisation/StringIds.h"
#include "../management/Finance.h"
#include "../ride/RideData.h"
#include "../windows/Intent.h"
#include "../world/Park.h"
#include "../world/Scenery.h"
#include "../world/Sprite.h"
#include "../world/Surface.h"
void LandLowerAction::Serialise(DataSerialiser& stream)
{
GameAction::Serialise(stream);
stream << DS_TAG(_coords) << DS_TAG(_range) << DS_TAG(_selectionType);
}
GameActions::Result::Ptr LandLowerAction::Query() const
{
return QueryExecute(false);
}
GameActions::Result::Ptr LandLowerAction::Execute() const
{
return QueryExecute(true);
}
GameActions::Result::Ptr LandLowerAction::QueryExecute(bool isExecuting) const
{
auto res = MakeResult();
size_t tableRow = _selectionType;
// The selections between MAP_SELECT_TYPE_FULL and MAP_SELECT_TYPE_EDGE_0 are not included in the tables
if (_selectionType >= MAP_SELECT_TYPE_EDGE_0 && _selectionType <= MAP_SELECT_TYPE_EDGE_3)
tableRow -= MAP_SELECT_TYPE_EDGE_0 - MAP_SELECT_TYPE_FULL - 1;
// Keep big coordinates within map boundaries
auto aX = std::max<decltype(_range.GetLeft())>(32, _range.GetLeft());
auto bX = std::min<decltype(_range.GetRight())>(gMapSizeMaxXY, _range.GetRight());
auto aY = std::max<decltype(_range.GetTop())>(32, _range.GetTop());
auto bY = std::min<decltype(_range.GetBottom())>(gMapSizeMaxXY, _range.GetBottom());
MapRange validRange = MapRange{ aX, aY, bX, bY };
res->Position = { _coords.x, _coords.y, tile_element_height(_coords) };
res->Expenditure = ExpenditureType::Landscaping;
if (isExecuting)
{
OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, { _coords.x, _coords.y, tile_element_height(_coords) });
}
uint8_t maxHeight = map_get_highest_land_height(validRange);
bool withinOwnership = false;
for (int32_t y = validRange.GetTop(); y <= validRange.GetBottom(); y += COORDS_XY_STEP)
{
for (int32_t x = validRange.GetLeft(); x <= validRange.GetRight(); x += COORDS_XY_STEP)
{
if (!LocationValid({ x, y }))
continue;
auto* surfaceElement = map_get_surface_element_at(CoordsXY{ x, y });
if (surfaceElement == nullptr)
continue;
if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode)
{
if (!map_is_location_in_park(CoordsXY{ x, y }))
{
continue;
}
}
withinOwnership = true;
uint8_t height = surfaceElement->base_height;
if (surfaceElement->GetSlope() & TILE_ELEMENT_SURFACE_RAISED_CORNERS_MASK)
height += 2;
if (surfaceElement->GetSlope() & TILE_ELEMENT_SURFACE_DIAGONAL_FLAG)
height += 2;
if (height < maxHeight)
continue;
height = surfaceElement->base_height;
uint8_t currentSlope = surfaceElement->GetSlope();
uint8_t newSlope = tile_element_lower_styles[tableRow][currentSlope];
if (newSlope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT)
height -= 2;
newSlope &= TILE_ELEMENT_SURFACE_SLOPE_MASK;
auto landSetHeightAction = LandSetHeightAction({ x, y }, height, newSlope);
landSetHeightAction.SetFlags(GetFlags());
auto result = isExecuting ? GameActions::ExecuteNested(&landSetHeightAction)
: GameActions::QueryNested(&landSetHeightAction);
if (result->Error == GameActions::Status::Ok)
{
res->Cost += result->Cost;
}
else
{
result->ErrorTitle = STR_CANT_LOWER_LAND_HERE;
return result;
}
}
}
if (!withinOwnership)
{
GameActions::Result::Ptr ownerShipResult = std::make_unique<GameActions::Result>(
GameActions::Status::Disallowed, STR_LAND_NOT_OWNED_BY_PARK);
ownerShipResult->ErrorTitle = STR_CANT_LOWER_LAND_HERE;
return ownerShipResult;
}
// Force ride construction to recheck area
_currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_RECHECK;
return res;
}

View File

@ -0,0 +1,41 @@
/*****************************************************************************
* 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
#include "GameAction.h"
DEFINE_GAME_ACTION(LandLowerAction, GAME_COMMAND_LOWER_LAND, GameActions::Result)
{
private:
CoordsXY _coords;
MapRange _range;
uint8_t _selectionType{};
public:
LandLowerAction() = default;
LandLowerAction(const CoordsXY& coords, MapRange range, uint8_t selectionType)
: _coords(coords)
, _range(range)
, _selectionType(selectionType)
{
}
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags();
}
void Serialise(DataSerialiser & stream) override;
GameActions::Result::Ptr Query() const override;
GameActions::Result::Ptr Execute() const override;
private:
GameActions::Result::Ptr QueryExecute(bool isExecuting) const;
};

View File

@ -1,161 +0,0 @@
/*****************************************************************************
* 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
#include "../Context.h"
#include "../OpenRCT2.h"
#include "../actions/LandSetHeightAction.hpp"
#include "../audio/audio.h"
#include "../interface/Window.h"
#include "../localisation/Localisation.h"
#include "../localisation/StringIds.h"
#include "../management/Finance.h"
#include "../ride/RideData.h"
#include "../windows/Intent.h"
#include "../world/Park.h"
#include "../world/Scenery.h"
#include "../world/Sprite.h"
#include "../world/Surface.h"
#include "GameAction.h"
DEFINE_GAME_ACTION(LandLowerAction, GAME_COMMAND_LOWER_LAND, GameActions::Result)
{
private:
CoordsXY _coords;
MapRange _range;
uint8_t _selectionType{};
public:
LandLowerAction() = default;
LandLowerAction(const CoordsXY& coords, MapRange range, uint8_t selectionType)
: _coords(coords)
, _range(range)
, _selectionType(selectionType)
{
}
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags();
}
void Serialise(DataSerialiser & stream) override
{
GameAction::Serialise(stream);
stream << DS_TAG(_coords) << DS_TAG(_range) << DS_TAG(_selectionType);
}
GameActions::Result::Ptr Query() const override
{
return QueryExecute(false);
}
GameActions::Result::Ptr Execute() const override
{
return QueryExecute(true);
}
private:
GameActions::Result::Ptr QueryExecute(bool isExecuting) const
{
auto res = MakeResult();
size_t tableRow = _selectionType;
// The selections between MAP_SELECT_TYPE_FULL and MAP_SELECT_TYPE_EDGE_0 are not included in the tables
if (_selectionType >= MAP_SELECT_TYPE_EDGE_0 && _selectionType <= MAP_SELECT_TYPE_EDGE_3)
tableRow -= MAP_SELECT_TYPE_EDGE_0 - MAP_SELECT_TYPE_FULL - 1;
// Keep big coordinates within map boundaries
auto aX = std::max<decltype(_range.GetLeft())>(32, _range.GetLeft());
auto bX = std::min<decltype(_range.GetRight())>(gMapSizeMaxXY, _range.GetRight());
auto aY = std::max<decltype(_range.GetTop())>(32, _range.GetTop());
auto bY = std::min<decltype(_range.GetBottom())>(gMapSizeMaxXY, _range.GetBottom());
MapRange validRange = MapRange{ aX, aY, bX, bY };
res->Position = { _coords.x, _coords.y, tile_element_height(_coords) };
res->Expenditure = ExpenditureType::Landscaping;
if (isExecuting)
{
OpenRCT2::Audio::Play3D(
OpenRCT2::Audio::SoundId::PlaceItem, { _coords.x, _coords.y, tile_element_height(_coords) });
}
uint8_t maxHeight = map_get_highest_land_height(validRange);
bool withinOwnership = false;
for (int32_t y = validRange.GetTop(); y <= validRange.GetBottom(); y += COORDS_XY_STEP)
{
for (int32_t x = validRange.GetLeft(); x <= validRange.GetRight(); x += COORDS_XY_STEP)
{
if (!LocationValid({ x, y }))
continue;
auto* surfaceElement = map_get_surface_element_at(CoordsXY{ x, y });
if (surfaceElement == nullptr)
continue;
if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode)
{
if (!map_is_location_in_park(CoordsXY{ x, y }))
{
continue;
}
}
withinOwnership = true;
uint8_t height = surfaceElement->base_height;
if (surfaceElement->GetSlope() & TILE_ELEMENT_SURFACE_RAISED_CORNERS_MASK)
height += 2;
if (surfaceElement->GetSlope() & TILE_ELEMENT_SURFACE_DIAGONAL_FLAG)
height += 2;
if (height < maxHeight)
continue;
height = surfaceElement->base_height;
uint8_t currentSlope = surfaceElement->GetSlope();
uint8_t newSlope = tile_element_lower_styles[tableRow][currentSlope];
if (newSlope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT)
height -= 2;
newSlope &= TILE_ELEMENT_SURFACE_SLOPE_MASK;
auto landSetHeightAction = LandSetHeightAction({ x, y }, height, newSlope);
landSetHeightAction.SetFlags(GetFlags());
auto result = isExecuting ? GameActions::ExecuteNested(&landSetHeightAction)
: GameActions::QueryNested(&landSetHeightAction);
if (result->Error == GameActions::Status::Ok)
{
res->Cost += result->Cost;
}
else
{
result->ErrorTitle = STR_CANT_LOWER_LAND_HERE;
return result;
}
}
}
if (!withinOwnership)
{
GameActions::Result::Ptr ownerShipResult = std::make_unique<GameActions::Result>(
GameActions::Status::Disallowed, STR_LAND_NOT_OWNED_BY_PARK);
ownerShipResult->ErrorTitle = STR_CANT_LOWER_LAND_HERE;
return ownerShipResult;
}
// Force ride construction to recheck area
_currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_RECHECK;
return res;
}
};

View File

@ -0,0 +1,132 @@
/*****************************************************************************
* 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.
*****************************************************************************/
#include "LandRaiseAction.h"
#include "../Context.h"
#include "../OpenRCT2.h"
#include "../actions/LandSetHeightAction.h"
#include "../audio/audio.h"
#include "../interface/Window.h"
#include "../localisation/Localisation.h"
#include "../localisation/StringIds.h"
#include "../management/Finance.h"
#include "../ride/RideData.h"
#include "../windows/Intent.h"
#include "../world/Park.h"
#include "../world/Scenery.h"
#include "../world/Sprite.h"
#include "../world/Surface.h"
void LandRaiseAction::Serialise(DataSerialiser& stream)
{
GameAction::Serialise(stream);
stream << DS_TAG(_coords) << DS_TAG(_range) << DS_TAG(_selectionType);
}
GameActions::Result::Ptr LandRaiseAction::Query() const
{
return QueryExecute(false);
}
GameActions::Result::Ptr LandRaiseAction::Execute() const
{
return QueryExecute(true);
}
GameActions::Result::Ptr LandRaiseAction::QueryExecute(bool isExecuting) const
{
auto res = MakeResult();
size_t tableRow = _selectionType;
// The selections between MAP_SELECT_TYPE_FULL and MAP_SELECT_TYPE_EDGE_0 are not included in the tables
if (_selectionType >= MAP_SELECT_TYPE_EDGE_0 && _selectionType <= MAP_SELECT_TYPE_EDGE_3)
tableRow -= MAP_SELECT_TYPE_EDGE_0 - MAP_SELECT_TYPE_FULL - 1;
// Keep big coordinates within map boundaries
auto aX = std::max<decltype(_range.GetLeft())>(32, _range.GetLeft());
auto bX = std::min<decltype(_range.GetRight())>(gMapSizeMaxXY, _range.GetRight());
auto aY = std::max<decltype(_range.GetTop())>(32, _range.GetTop());
auto bY = std::min<decltype(_range.GetBottom())>(gMapSizeMaxXY, _range.GetBottom());
MapRange validRange = MapRange{ aX, aY, bX, bY };
res->Position = { _coords.x, _coords.y, tile_element_height(_coords) };
res->Expenditure = ExpenditureType::Landscaping;
if (isExecuting)
{
OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, { _coords.x, _coords.y, tile_element_height(_coords) });
}
uint8_t minHeight = map_get_lowest_land_height(validRange);
bool withinOwnership = false;
for (int32_t y = validRange.GetTop(); y <= validRange.GetBottom(); y += COORDS_XY_STEP)
{
for (int32_t x = validRange.GetLeft(); x <= validRange.GetRight(); x += COORDS_XY_STEP)
{
if (!LocationValid({ x, y }))
continue;
auto* surfaceElement = map_get_surface_element_at(CoordsXY{ x, y });
if (surfaceElement == nullptr)
continue;
if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode)
{
if (!map_is_location_in_park(CoordsXY{ x, y }))
{
continue;
}
}
withinOwnership = true;
uint8_t height = surfaceElement->base_height;
if (height > minHeight)
continue;
uint8_t currentSlope = surfaceElement->GetSlope();
uint8_t newSlope = tile_element_raise_styles[tableRow][currentSlope];
if (newSlope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT)
height += 2;
newSlope &= TILE_ELEMENT_SURFACE_SLOPE_MASK;
auto landSetHeightAction = LandSetHeightAction({ x, y }, height, newSlope);
landSetHeightAction.SetFlags(GetFlags());
auto result = isExecuting ? GameActions::ExecuteNested(&landSetHeightAction)
: GameActions::QueryNested(&landSetHeightAction);
if (result->Error == GameActions::Status::Ok)
{
res->Cost += result->Cost;
}
else
{
result->ErrorTitle = STR_CANT_RAISE_LAND_HERE;
return result;
}
}
}
if (!withinOwnership)
{
GameActions::Result::Ptr ownerShipResult = std::make_unique<GameActions::Result>(
GameActions::Status::Disallowed, STR_LAND_NOT_OWNED_BY_PARK);
ownerShipResult->ErrorTitle = STR_CANT_RAISE_LAND_HERE;
return ownerShipResult;
}
// Force ride construction to recheck area
_currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_RECHECK;
return res;
}

View File

@ -0,0 +1,41 @@
/*****************************************************************************
* 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
#include "GameAction.h"
DEFINE_GAME_ACTION(LandRaiseAction, GAME_COMMAND_RAISE_LAND, GameActions::Result)
{
private:
CoordsXY _coords;
MapRange _range;
uint8_t _selectionType{};
public:
LandRaiseAction() = default;
LandRaiseAction(const CoordsXY& coords, MapRange range, uint8_t selectionType)
: _coords(coords)
, _range(range)
, _selectionType(selectionType)
{
}
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags();
}
void Serialise(DataSerialiser & stream) override;
GameActions::Result::Ptr Query() const override;
GameActions::Result::Ptr Execute() const override;
private:
GameActions::Result::Ptr QueryExecute(bool isExecuting) const;
};

View File

@ -1,157 +0,0 @@
/*****************************************************************************
* 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
#include "../Context.h"
#include "../OpenRCT2.h"
#include "../actions/LandSetHeightAction.hpp"
#include "../audio/audio.h"
#include "../interface/Window.h"
#include "../localisation/Localisation.h"
#include "../localisation/StringIds.h"
#include "../management/Finance.h"
#include "../ride/RideData.h"
#include "../windows/Intent.h"
#include "../world/Park.h"
#include "../world/Scenery.h"
#include "../world/Sprite.h"
#include "../world/Surface.h"
#include "GameAction.h"
DEFINE_GAME_ACTION(LandRaiseAction, GAME_COMMAND_RAISE_LAND, GameActions::Result)
{
private:
CoordsXY _coords;
MapRange _range;
uint8_t _selectionType{};
public:
LandRaiseAction() = default;
LandRaiseAction(const CoordsXY& coords, MapRange range, uint8_t selectionType)
: _coords(coords)
, _range(range)
, _selectionType(selectionType)
{
}
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags();
}
void Serialise(DataSerialiser & stream) override
{
GameAction::Serialise(stream);
stream << DS_TAG(_coords) << DS_TAG(_range) << DS_TAG(_selectionType);
}
GameActions::Result::Ptr Query() const override
{
return QueryExecute(false);
}
GameActions::Result::Ptr Execute() const override
{
return QueryExecute(true);
}
private:
GameActions::Result::Ptr QueryExecute(bool isExecuting) const
{
auto res = MakeResult();
size_t tableRow = _selectionType;
// The selections between MAP_SELECT_TYPE_FULL and MAP_SELECT_TYPE_EDGE_0 are not included in the tables
if (_selectionType >= MAP_SELECT_TYPE_EDGE_0 && _selectionType <= MAP_SELECT_TYPE_EDGE_3)
tableRow -= MAP_SELECT_TYPE_EDGE_0 - MAP_SELECT_TYPE_FULL - 1;
// Keep big coordinates within map boundaries
auto aX = std::max<decltype(_range.GetLeft())>(32, _range.GetLeft());
auto bX = std::min<decltype(_range.GetRight())>(gMapSizeMaxXY, _range.GetRight());
auto aY = std::max<decltype(_range.GetTop())>(32, _range.GetTop());
auto bY = std::min<decltype(_range.GetBottom())>(gMapSizeMaxXY, _range.GetBottom());
MapRange validRange = MapRange{ aX, aY, bX, bY };
res->Position = { _coords.x, _coords.y, tile_element_height(_coords) };
res->Expenditure = ExpenditureType::Landscaping;
if (isExecuting)
{
OpenRCT2::Audio::Play3D(
OpenRCT2::Audio::SoundId::PlaceItem, { _coords.x, _coords.y, tile_element_height(_coords) });
}
uint8_t minHeight = map_get_lowest_land_height(validRange);
bool withinOwnership = false;
for (int32_t y = validRange.GetTop(); y <= validRange.GetBottom(); y += COORDS_XY_STEP)
{
for (int32_t x = validRange.GetLeft(); x <= validRange.GetRight(); x += COORDS_XY_STEP)
{
if (!LocationValid({ x, y }))
continue;
auto* surfaceElement = map_get_surface_element_at(CoordsXY{ x, y });
if (surfaceElement == nullptr)
continue;
if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode)
{
if (!map_is_location_in_park(CoordsXY{ x, y }))
{
continue;
}
}
withinOwnership = true;
uint8_t height = surfaceElement->base_height;
if (height > minHeight)
continue;
uint8_t currentSlope = surfaceElement->GetSlope();
uint8_t newSlope = tile_element_raise_styles[tableRow][currentSlope];
if (newSlope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT)
height += 2;
newSlope &= TILE_ELEMENT_SURFACE_SLOPE_MASK;
auto landSetHeightAction = LandSetHeightAction({ x, y }, height, newSlope);
landSetHeightAction.SetFlags(GetFlags());
auto result = isExecuting ? GameActions::ExecuteNested(&landSetHeightAction)
: GameActions::QueryNested(&landSetHeightAction);
if (result->Error == GameActions::Status::Ok)
{
res->Cost += result->Cost;
}
else
{
result->ErrorTitle = STR_CANT_RAISE_LAND_HERE;
return result;
}
}
}
if (!withinOwnership)
{
GameActions::Result::Ptr ownerShipResult = std::make_unique<GameActions::Result>(
GameActions::Status::Disallowed, STR_LAND_NOT_OWNED_BY_PARK);
ownerShipResult->ErrorTitle = STR_CANT_RAISE_LAND_HERE;
return ownerShipResult;
}
// Force ride construction to recheck area
_currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_RECHECK;
return res;
}
};

View File

@ -0,0 +1,374 @@
/*****************************************************************************
* 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.
*****************************************************************************/
#include "LandSetHeightAction.h"
#include "../Context.h"
#include "../OpenRCT2.h"
#include "../interface/Window.h"
#include "../localisation/Localisation.h"
#include "../localisation/StringIds.h"
#include "../management/Finance.h"
#include "../ride/RideData.h"
#include "../windows/Intent.h"
#include "../world/Park.h"
#include "../world/Scenery.h"
#include "../world/SmallScenery.h"
#include "../world/Sprite.h"
#include "../world/Surface.h"
void LandSetHeightAction::Serialise(DataSerialiser& stream)
{
GameAction::Serialise(stream);
stream << DS_TAG(_coords) << DS_TAG(_height) << DS_TAG(_style);
}
GameActions::Result::Ptr LandSetHeightAction::Query() const
{
if (gParkFlags & PARK_FLAGS_FORBID_LANDSCAPE_CHANGES)
{
return std::make_unique<GameActions::Result>(GameActions::Status::Disallowed, STR_FORBIDDEN_BY_THE_LOCAL_AUTHORITY);
}
rct_string_id errorTitle = CheckParameters();
if (errorTitle != STR_NONE)
{
return std::make_unique<GameActions::Result>(GameActions::Status::Disallowed, errorTitle);
}
if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode)
{
if (!map_is_location_in_park(_coords))
{
return std::make_unique<GameActions::Result>(GameActions::Status::Disallowed, STR_LAND_NOT_OWNED_BY_PARK);
}
}
money32 sceneryRemovalCost = 0;
if (!gCheatsDisableClearanceChecks)
{
if (gParkFlags & PARK_FLAGS_FORBID_TREE_REMOVAL)
{
// Check for obstructing large trees
TileElement* tileElement = CheckTreeObstructions();
if (tileElement != nullptr)
{
auto res = MakeResult(GameActions::Status::Disallowed, STR_NONE);
map_obstruction_set_error_text(tileElement, *res);
return res;
}
}
sceneryRemovalCost = GetSmallSceneryRemovalCost();
}
// Check for ride support limits
if (!gCheatsDisableSupportLimits)
{
errorTitle = CheckRideSupports();
if (errorTitle != STR_NONE)
{
return std::make_unique<GameActions::Result>(GameActions::Status::Disallowed, errorTitle);
}
}
auto* surfaceElement = map_get_surface_element_at(_coords);
if (surfaceElement == nullptr)
return std::make_unique<GameActions::Result>(GameActions::Status::Unknown, STR_NONE);
// We need to check if there is _currently_ a level crossing on the tile.
// For that, we need the old height, so we can't use the _height variable.
auto oldCoords = CoordsXYZ{ _coords, surfaceElement->GetBaseZ() };
auto* pathElement = map_get_footpath_element(oldCoords);
if (pathElement != nullptr && pathElement->AsPath()->IsLevelCrossing(oldCoords))
{
return MakeResult(GameActions::Status::Disallowed, STR_REMOVE_LEVEL_CROSSING_FIRST);
}
TileElement* tileElement = CheckFloatingStructures(reinterpret_cast<TileElement*>(surfaceElement), _height);
if (tileElement != nullptr)
{
auto res = MakeResult(GameActions::Status::Disallowed, STR_NONE);
map_obstruction_set_error_text(tileElement, *res);
return res;
}
if (!gCheatsDisableClearanceChecks)
{
uint8_t zCorner = _height;
if (_style & TILE_ELEMENT_SURFACE_RAISED_CORNERS_MASK)
{
zCorner += 2;
if (_style & TILE_ELEMENT_SURFACE_DIAGONAL_FLAG)
{
zCorner += 2;
}
}
auto clearResult = MapCanConstructWithClearAt(
{ _coords, _height * COORDS_Z_STEP, zCorner * COORDS_Z_STEP }, &map_set_land_height_clear_func, { 0b1111, 0 }, 0,
CREATE_CROSSING_MODE_NONE);
if (clearResult->Error != GameActions::Status::Ok)
{
return std::make_unique<GameActions::Result>(
GameActions::Status::Disallowed, STR_NONE, clearResult->ErrorMessage.GetStringId(),
clearResult->ErrorMessageArgs.data());
}
tileElement = CheckUnremovableObstructions(reinterpret_cast<TileElement*>(surfaceElement), zCorner);
if (tileElement != nullptr)
{
auto res = MakeResult(GameActions::Status::Disallowed, STR_NONE);
map_obstruction_set_error_text(tileElement, *res);
return res;
}
}
auto res = std::make_unique<GameActions::Result>();
res->Cost = sceneryRemovalCost + GetSurfaceHeightChangeCost(surfaceElement);
res->Expenditure = ExpenditureType::Landscaping;
return res;
}
GameActions::Result::Ptr LandSetHeightAction::Execute() const
{
money32 cost = MONEY(0, 0);
auto surfaceHeight = tile_element_height(_coords);
footpath_remove_litter({ _coords, surfaceHeight });
if (!gCheatsDisableClearanceChecks)
{
wall_remove_at({ _coords, _height * 8 - 16, _height * 8 + 32 });
cost += GetSmallSceneryRemovalCost();
SmallSceneryRemoval();
}
auto* surfaceElement = map_get_surface_element_at(_coords);
if (surfaceElement == nullptr)
return std::make_unique<GameActions::Result>(GameActions::Status::Unknown, STR_NONE);
cost += GetSurfaceHeightChangeCost(surfaceElement);
SetSurfaceHeight(reinterpret_cast<TileElement*>(surfaceElement));
auto res = std::make_unique<GameActions::Result>();
res->Position = { _coords.x + 16, _coords.y + 16, surfaceHeight };
res->Cost = cost;
res->Expenditure = ExpenditureType::Landscaping;
return res;
}
rct_string_id LandSetHeightAction::CheckParameters() const
{
if (!LocationValid(_coords))
{
return STR_OFF_EDGE_OF_MAP;
}
if (_coords.x > gMapSizeMaxXY || _coords.y > gMapSizeMaxXY)
{
return STR_OFF_EDGE_OF_MAP;
}
if (_height < MINIMUM_LAND_HEIGHT)
{
return STR_TOO_LOW;
}
// Divide by 2 and subtract 7 to get the in-game units.
if (_height > MAXIMUM_LAND_HEIGHT)
{
return STR_TOO_HIGH;
}
else if (_height > MAXIMUM_LAND_HEIGHT - 2 && (_style & TILE_ELEMENT_SURFACE_SLOPE_MASK) != 0)
{
return STR_TOO_HIGH;
}
if (_height == MAXIMUM_LAND_HEIGHT - 2 && (_style & TILE_ELEMENT_SURFACE_DIAGONAL_FLAG))
{
return STR_TOO_HIGH;
}
return STR_NONE;
}
TileElement* LandSetHeightAction::CheckTreeObstructions() const
{
TileElement* tileElement = map_get_first_element_at(_coords);
do
{
if (tileElement == nullptr)
break;
if (tileElement->GetType() != TILE_ELEMENT_TYPE_SMALL_SCENERY)
continue;
if (_height > tileElement->clearance_height)
continue;
if (_height + 4 < tileElement->base_height)
continue;
rct_scenery_entry* sceneryEntry = tileElement->AsSmallScenery()->GetEntry();
if (scenery_small_entry_has_flag(sceneryEntry, SMALL_SCENERY_FLAG_IS_TREE))
{
return tileElement;
}
} while (!(tileElement++)->IsLastForTile());
return nullptr;
}
money32 LandSetHeightAction::GetSmallSceneryRemovalCost() const
{
money32 cost{ 0 };
TileElement* tileElement = map_get_first_element_at(_coords);
do
{
if (tileElement == nullptr)
break;
if (tileElement->GetType() != TILE_ELEMENT_TYPE_SMALL_SCENERY)
continue;
if (_height > tileElement->clearance_height)
continue;
if (_height + 4 < tileElement->base_height)
continue;
rct_scenery_entry* sceneryEntry = tileElement->AsSmallScenery()->GetEntry();
cost += MONEY(sceneryEntry->small_scenery.removal_price, 0);
} while (!(tileElement++)->IsLastForTile());
return cost;
}
void LandSetHeightAction::SmallSceneryRemoval() const
{
TileElement* tileElement = map_get_first_element_at(_coords);
do
{
if (tileElement == nullptr)
break;
if (tileElement->GetType() != TILE_ELEMENT_TYPE_SMALL_SCENERY)
continue;
if (_height > tileElement->clearance_height)
continue;
if (_height + 4 < tileElement->base_height)
continue;
tile_element_remove(tileElement--);
} while (!(tileElement++)->IsLastForTile());
}
rct_string_id LandSetHeightAction::CheckRideSupports() const
{
TileElement* tileElement = map_get_first_element_at(_coords);
do
{
if (tileElement == nullptr)
break;
if (tileElement->GetType() == TILE_ELEMENT_TYPE_TRACK)
{
ride_id_t rideIndex = tileElement->AsTrack()->GetRideIndex();
auto ride = get_ride(rideIndex);
if (ride != nullptr)
{
rct_ride_entry* rideEntry = ride->GetRideEntry();
if (rideEntry != nullptr)
{
int32_t maxHeight = rideEntry->max_height;
if (maxHeight == 0)
{
maxHeight = RideTypeDescriptors[ride->type].Heights.MaxHeight;
}
int32_t zDelta = tileElement->clearance_height - _height;
if (zDelta >= 0 && zDelta / 2 > maxHeight)
{
return STR_SUPPORTS_CANT_BE_EXTENDED;
}
}
}
}
} while (!(tileElement++)->IsLastForTile());
return STR_NONE;
}
TileElement* LandSetHeightAction::CheckFloatingStructures(TileElement* surfaceElement, uint8_t zCorner) const
{
if (surfaceElement->AsSurface()->HasTrackThatNeedsWater())
{
uint32_t waterHeight = surfaceElement->AsSurface()->GetWaterHeight();
if (waterHeight != 0)
{
if (_style & TILE_ELEMENT_SURFACE_SLOPE_MASK)
{
zCorner += 2;
if (_style & TILE_ELEMENT_SURFACE_DIAGONAL_FLAG)
{
zCorner += 2;
}
}
if (zCorner > (waterHeight / COORDS_Z_STEP) - 2)
{
return ++surfaceElement;
}
}
}
return nullptr;
}
TileElement* LandSetHeightAction::CheckUnremovableObstructions(TileElement* surfaceElement, uint8_t zCorner) const
{
TileElement* tileElement = map_get_first_element_at(_coords);
do
{
if (tileElement == nullptr)
break;
int32_t elementType = tileElement->GetType();
// Wall's and Small Scenery are removed and therefore do not need checked
if (elementType == TILE_ELEMENT_TYPE_WALL)
continue;
if (elementType == TILE_ELEMENT_TYPE_SMALL_SCENERY)
continue;
if (tileElement->IsGhost())
continue;
if (tileElement == surfaceElement)
continue;
if (tileElement > surfaceElement)
{
if (zCorner > tileElement->base_height)
{
return tileElement;
}
continue;
}
if (_height < tileElement->clearance_height)
{
return tileElement;
}
} while (!(tileElement++)->IsLastForTile());
return nullptr;
}
money32 LandSetHeightAction::GetSurfaceHeightChangeCost(SurfaceElement* surfaceElement) const
{
money32 cost{ 0 };
for (Direction i : ALL_DIRECTIONS)
{
int32_t cornerHeight = tile_element_get_corner_height(surfaceElement, i);
cornerHeight -= map_get_corner_height(_height, _style & TILE_ELEMENT_SURFACE_SLOPE_MASK, i);
cost += MONEY(abs(cornerHeight) * 5 / 2, 0);
}
return cost;
}
void LandSetHeightAction::SetSurfaceHeight(TileElement* surfaceElement) const
{
surfaceElement->base_height = _height;
surfaceElement->clearance_height = _height;
surfaceElement->AsSurface()->SetSlope(_style);
int32_t waterHeight = surfaceElement->AsSurface()->GetWaterHeight() / COORDS_Z_STEP;
if (waterHeight != 0 && waterHeight <= _height)
{
surfaceElement->AsSurface()->SetWaterHeight(0);
}
map_invalidate_tile_full(_coords);
}

View File

@ -0,0 +1,66 @@
/*****************************************************************************
* 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
#include "GameAction.h"
DEFINE_GAME_ACTION(LandSetHeightAction, GAME_COMMAND_SET_LAND_HEIGHT, GameActions::Result)
{
private:
CoordsXY _coords;
uint8_t _height{};
uint8_t _style{};
public:
LandSetHeightAction() = default;
LandSetHeightAction(const CoordsXY& coords, uint8_t height, uint8_t style)
: _coords(coords)
, _height(height)
, _style(style)
{
}
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags() | GameActions::Flags::EditorOnly;
}
void Serialise(DataSerialiser & stream) override;
GameActions::Result::Ptr Query() const override;
GameActions::Result::Ptr Execute() const override;
private:
rct_string_id CheckParameters() const;
TileElement* CheckTreeObstructions() const;
money32 GetSmallSceneryRemovalCost() const;
void SmallSceneryRemoval() const;
rct_string_id CheckRideSupports() const;
TileElement* CheckFloatingStructures(TileElement * surfaceElement, uint8_t zCorner) const;
TileElement* CheckUnremovableObstructions(TileElement * surfaceElement, uint8_t zCorner) const;
money32 GetSurfaceHeightChangeCost(SurfaceElement * surfaceElement) const;
void SetSurfaceHeight(TileElement * surfaceElement) const;
/**
*
* rct2: 0x00663CB9
*/
static int32_t map_set_land_height_clear_func(
TileElement * *tile_element, [[maybe_unused]] const CoordsXY& coords, [[maybe_unused]] uint8_t flags,
[[maybe_unused]] money32* price)
{
if ((*tile_element)->GetType() == TILE_ELEMENT_TYPE_SURFACE)
return 0;
if ((*tile_element)->GetType() == TILE_ELEMENT_TYPE_SMALL_SCENERY)
return 0;
return 1;
}
};

View File

@ -1,415 +0,0 @@
/*****************************************************************************
* 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
#include "../Context.h"
#include "../OpenRCT2.h"
#include "../interface/Window.h"
#include "../localisation/Localisation.h"
#include "../localisation/StringIds.h"
#include "../management/Finance.h"
#include "../ride/RideData.h"
#include "../windows/Intent.h"
#include "../world/Park.h"
#include "../world/Scenery.h"
#include "../world/SmallScenery.h"
#include "../world/Sprite.h"
#include "../world/Surface.h"
#include "GameAction.h"
DEFINE_GAME_ACTION(LandSetHeightAction, GAME_COMMAND_SET_LAND_HEIGHT, GameActions::Result)
{
private:
CoordsXY _coords;
uint8_t _height{};
uint8_t _style{};
public:
LandSetHeightAction() = default;
LandSetHeightAction(const CoordsXY& coords, uint8_t height, uint8_t style)
: _coords(coords)
, _height(height)
, _style(style)
{
}
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags() | GameActions::Flags::EditorOnly;
}
void Serialise(DataSerialiser & stream) override
{
GameAction::Serialise(stream);
stream << DS_TAG(_coords) << DS_TAG(_height) << DS_TAG(_style);
}
GameActions::Result::Ptr Query() const override
{
if (gParkFlags & PARK_FLAGS_FORBID_LANDSCAPE_CHANGES)
{
return std::make_unique<GameActions::Result>(GameActions::Status::Disallowed, STR_FORBIDDEN_BY_THE_LOCAL_AUTHORITY);
}
rct_string_id errorTitle = CheckParameters();
if (errorTitle != STR_NONE)
{
return std::make_unique<GameActions::Result>(GameActions::Status::Disallowed, errorTitle);
}
if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode)
{
if (!map_is_location_in_park(_coords))
{
return std::make_unique<GameActions::Result>(GameActions::Status::Disallowed, STR_LAND_NOT_OWNED_BY_PARK);
}
}
money32 sceneryRemovalCost = 0;
if (!gCheatsDisableClearanceChecks)
{
if (gParkFlags & PARK_FLAGS_FORBID_TREE_REMOVAL)
{
// Check for obstructing large trees
TileElement* tileElement = CheckTreeObstructions();
if (tileElement != nullptr)
{
auto res = MakeResult(GameActions::Status::Disallowed, STR_NONE);
map_obstruction_set_error_text(tileElement, *res);
return res;
}
}
sceneryRemovalCost = GetSmallSceneryRemovalCost();
}
// Check for ride support limits
if (!gCheatsDisableSupportLimits)
{
errorTitle = CheckRideSupports();
if (errorTitle != STR_NONE)
{
return std::make_unique<GameActions::Result>(GameActions::Status::Disallowed, errorTitle);
}
}
auto* surfaceElement = map_get_surface_element_at(_coords);
if (surfaceElement == nullptr)
return std::make_unique<GameActions::Result>(GameActions::Status::Unknown, STR_NONE);
// We need to check if there is _currently_ a level crossing on the tile.
// For that, we need the old height, so we can't use the _height variable.
auto oldCoords = CoordsXYZ{ _coords, surfaceElement->GetBaseZ() };
auto* pathElement = map_get_footpath_element(oldCoords);
if (pathElement != nullptr && pathElement->AsPath()->IsLevelCrossing(oldCoords))
{
return MakeResult(GameActions::Status::Disallowed, STR_REMOVE_LEVEL_CROSSING_FIRST);
}
TileElement* tileElement = CheckFloatingStructures(reinterpret_cast<TileElement*>(surfaceElement), _height);
if (tileElement != nullptr)
{
auto res = MakeResult(GameActions::Status::Disallowed, STR_NONE);
map_obstruction_set_error_text(tileElement, *res);
return res;
}
if (!gCheatsDisableClearanceChecks)
{
uint8_t zCorner = _height;
if (_style & TILE_ELEMENT_SURFACE_RAISED_CORNERS_MASK)
{
zCorner += 2;
if (_style & TILE_ELEMENT_SURFACE_DIAGONAL_FLAG)
{
zCorner += 2;
}
}
auto clearResult = MapCanConstructWithClearAt(
{ _coords, _height * COORDS_Z_STEP, zCorner * COORDS_Z_STEP }, &map_set_land_height_clear_func, { 0b1111, 0 },
0, CREATE_CROSSING_MODE_NONE);
if (clearResult->Error != GameActions::Status::Ok)
{
return std::make_unique<GameActions::Result>(
GameActions::Status::Disallowed, STR_NONE, clearResult->ErrorMessage.GetStringId(),
clearResult->ErrorMessageArgs.data());
}
tileElement = CheckUnremovableObstructions(reinterpret_cast<TileElement*>(surfaceElement), zCorner);
if (tileElement != nullptr)
{
auto res = MakeResult(GameActions::Status::Disallowed, STR_NONE);
map_obstruction_set_error_text(tileElement, *res);
return res;
}
}
auto res = std::make_unique<GameActions::Result>();
res->Cost = sceneryRemovalCost + GetSurfaceHeightChangeCost(surfaceElement);
res->Expenditure = ExpenditureType::Landscaping;
return res;
}
GameActions::Result::Ptr Execute() const override
{
money32 cost = MONEY(0, 0);
auto surfaceHeight = tile_element_height(_coords);
footpath_remove_litter({ _coords, surfaceHeight });
if (!gCheatsDisableClearanceChecks)
{
wall_remove_at({ _coords, _height * 8 - 16, _height * 8 + 32 });
cost += GetSmallSceneryRemovalCost();
SmallSceneryRemoval();
}
auto* surfaceElement = map_get_surface_element_at(_coords);
if (surfaceElement == nullptr)
return std::make_unique<GameActions::Result>(GameActions::Status::Unknown, STR_NONE);
cost += GetSurfaceHeightChangeCost(surfaceElement);
SetSurfaceHeight(reinterpret_cast<TileElement*>(surfaceElement));
auto res = std::make_unique<GameActions::Result>();
res->Position = { _coords.x + 16, _coords.y + 16, surfaceHeight };
res->Cost = cost;
res->Expenditure = ExpenditureType::Landscaping;
return res;
}
private:
rct_string_id CheckParameters() const
{
if (!LocationValid(_coords))
{
return STR_OFF_EDGE_OF_MAP;
}
if (_coords.x > gMapSizeMaxXY || _coords.y > gMapSizeMaxXY)
{
return STR_OFF_EDGE_OF_MAP;
}
if (_height < MINIMUM_LAND_HEIGHT)
{
return STR_TOO_LOW;
}
// Divide by 2 and subtract 7 to get the in-game units.
if (_height > MAXIMUM_LAND_HEIGHT)
{
return STR_TOO_HIGH;
}
else if (_height > MAXIMUM_LAND_HEIGHT - 2 && (_style & TILE_ELEMENT_SURFACE_SLOPE_MASK) != 0)
{
return STR_TOO_HIGH;
}
if (_height == MAXIMUM_LAND_HEIGHT - 2 && (_style & TILE_ELEMENT_SURFACE_DIAGONAL_FLAG))
{
return STR_TOO_HIGH;
}
return STR_NONE;
}
TileElement* CheckTreeObstructions() const
{
TileElement* tileElement = map_get_first_element_at(_coords);
do
{
if (tileElement == nullptr)
break;
if (tileElement->GetType() != TILE_ELEMENT_TYPE_SMALL_SCENERY)
continue;
if (_height > tileElement->clearance_height)
continue;
if (_height + 4 < tileElement->base_height)
continue;
rct_scenery_entry* sceneryEntry = tileElement->AsSmallScenery()->GetEntry();
if (scenery_small_entry_has_flag(sceneryEntry, SMALL_SCENERY_FLAG_IS_TREE))
{
return tileElement;
}
} while (!(tileElement++)->IsLastForTile());
return nullptr;
}
money32 GetSmallSceneryRemovalCost() const
{
money32 cost{ 0 };
TileElement* tileElement = map_get_first_element_at(_coords);
do
{
if (tileElement == nullptr)
break;
if (tileElement->GetType() != TILE_ELEMENT_TYPE_SMALL_SCENERY)
continue;
if (_height > tileElement->clearance_height)
continue;
if (_height + 4 < tileElement->base_height)
continue;
rct_scenery_entry* sceneryEntry = tileElement->AsSmallScenery()->GetEntry();
cost += MONEY(sceneryEntry->small_scenery.removal_price, 0);
} while (!(tileElement++)->IsLastForTile());
return cost;
}
void SmallSceneryRemoval() const
{
TileElement* tileElement = map_get_first_element_at(_coords);
do
{
if (tileElement == nullptr)
break;
if (tileElement->GetType() != TILE_ELEMENT_TYPE_SMALL_SCENERY)
continue;
if (_height > tileElement->clearance_height)
continue;
if (_height + 4 < tileElement->base_height)
continue;
tile_element_remove(tileElement--);
} while (!(tileElement++)->IsLastForTile());
}
rct_string_id CheckRideSupports() const
{
TileElement* tileElement = map_get_first_element_at(_coords);
do
{
if (tileElement == nullptr)
break;
if (tileElement->GetType() == TILE_ELEMENT_TYPE_TRACK)
{
ride_id_t rideIndex = tileElement->AsTrack()->GetRideIndex();
auto ride = get_ride(rideIndex);
if (ride != nullptr)
{
rct_ride_entry* rideEntry = ride->GetRideEntry();
if (rideEntry != nullptr)
{
int32_t maxHeight = rideEntry->max_height;
if (maxHeight == 0)
{
maxHeight = RideTypeDescriptors[ride->type].Heights.MaxHeight;
}
int32_t zDelta = tileElement->clearance_height - _height;
if (zDelta >= 0 && zDelta / 2 > maxHeight)
{
return STR_SUPPORTS_CANT_BE_EXTENDED;
}
}
}
}
} while (!(tileElement++)->IsLastForTile());
return STR_NONE;
}
TileElement* CheckFloatingStructures(TileElement * surfaceElement, uint8_t zCorner) const
{
if (surfaceElement->AsSurface()->HasTrackThatNeedsWater())
{
uint32_t waterHeight = surfaceElement->AsSurface()->GetWaterHeight();
if (waterHeight != 0)
{
if (_style & TILE_ELEMENT_SURFACE_SLOPE_MASK)
{
zCorner += 2;
if (_style & TILE_ELEMENT_SURFACE_DIAGONAL_FLAG)
{
zCorner += 2;
}
}
if (zCorner > (waterHeight / COORDS_Z_STEP) - 2)
{
return ++surfaceElement;
}
}
}
return nullptr;
}
TileElement* CheckUnremovableObstructions(TileElement * surfaceElement, uint8_t zCorner) const
{
TileElement* tileElement = map_get_first_element_at(_coords);
do
{
if (tileElement == nullptr)
break;
int32_t elementType = tileElement->GetType();
// Wall's and Small Scenery are removed and therefore do not need checked
if (elementType == TILE_ELEMENT_TYPE_WALL)
continue;
if (elementType == TILE_ELEMENT_TYPE_SMALL_SCENERY)
continue;
if (tileElement->IsGhost())
continue;
if (tileElement == surfaceElement)
continue;
if (tileElement > surfaceElement)
{
if (zCorner > tileElement->base_height)
{
return tileElement;
}
continue;
}
if (_height < tileElement->clearance_height)
{
return tileElement;
}
} while (!(tileElement++)->IsLastForTile());
return nullptr;
}
money32 GetSurfaceHeightChangeCost(SurfaceElement * surfaceElement) const
{
money32 cost{ 0 };
for (Direction i : ALL_DIRECTIONS)
{
int32_t cornerHeight = tile_element_get_corner_height(surfaceElement, i);
cornerHeight -= map_get_corner_height(_height, _style & TILE_ELEMENT_SURFACE_SLOPE_MASK, i);
cost += MONEY(abs(cornerHeight) * 5 / 2, 0);
}
return cost;
}
void SetSurfaceHeight(TileElement * surfaceElement) const
{
surfaceElement->base_height = _height;
surfaceElement->clearance_height = _height;
surfaceElement->AsSurface()->SetSlope(_style);
int32_t waterHeight = surfaceElement->AsSurface()->GetWaterHeight() / COORDS_Z_STEP;
if (waterHeight != 0 && waterHeight <= _height)
{
surfaceElement->AsSurface()->SetWaterHeight(0);
}
map_invalidate_tile_full(_coords);
}
/**
*
* rct2: 0x00663CB9
*/
static int32_t map_set_land_height_clear_func(
TileElement * *tile_element, [[maybe_unused]] const CoordsXY& coords, [[maybe_unused]] uint8_t flags,
[[maybe_unused]] money32* price)
{
if ((*tile_element)->GetType() == TILE_ELEMENT_TYPE_SURFACE)
return 0;
if ((*tile_element)->GetType() == TILE_ELEMENT_TYPE_SMALL_SCENERY)
return 0;
return 1;
}
};

View File

@ -0,0 +1,193 @@
/*****************************************************************************
* 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.
*****************************************************************************/
#include "LandSetRightsAction.h"
#include "../Context.h"
#include "../OpenRCT2.h"
#include "../actions/LandSetHeightAction.h"
#include "../audio/audio.h"
#include "../interface/Window.h"
#include "../localisation/Localisation.h"
#include "../localisation/StringIds.h"
#include "../management/Finance.h"
#include "../ride/RideData.h"
#include "../util/Util.h"
#include "../windows/Intent.h"
#include "../world/Park.h"
#include "../world/Scenery.h"
#include "../world/Sprite.h"
#include "../world/Surface.h"
void LandSetRightsAction::Serialise(DataSerialiser& stream)
{
GameAction::Serialise(stream);
stream << DS_TAG(_range) << DS_TAG(_setting) << DS_TAG(_ownership);
}
GameActions::Result::Ptr LandSetRightsAction::Query() const
{
return QueryExecute(false);
}
GameActions::Result::Ptr LandSetRightsAction::Execute() const
{
return QueryExecute(true);
}
GameActions::Result::Ptr LandSetRightsAction::QueryExecute(bool isExecuting) const
{
auto res = MakeResult();
MapRange normRange = _range.Normalise();
// Keep big coordinates within map boundaries
auto aX = std::max<decltype(normRange.GetLeft())>(32, normRange.GetLeft());
auto bX = std::min<decltype(normRange.GetRight())>(gMapSizeMaxXY, normRange.GetRight());
auto aY = std::max<decltype(normRange.GetTop())>(32, normRange.GetTop());
auto bY = std::min<decltype(normRange.GetBottom())>(gMapSizeMaxXY, normRange.GetBottom());
MapRange validRange = MapRange{ aX, aY, bX, bY };
CoordsXYZ centre{ (validRange.GetLeft() + validRange.GetRight()) / 2 + 16,
(validRange.GetTop() + validRange.GetBottom()) / 2 + 16, 0 };
centre.z = tile_element_height(centre);
res->Position = centre;
res->Expenditure = ExpenditureType::LandPurchase;
if (!(gScreenFlags & SCREEN_FLAGS_EDITOR) && !gCheatsSandboxMode)
{
return MakeResult(GameActions::Status::NotInEditorMode, STR_NONE, STR_LAND_NOT_FOR_SALE);
}
// Game command modified to accept selection size
for (auto y = validRange.GetTop(); y <= validRange.GetBottom(); y += COORDS_XY_STEP)
{
for (auto x = validRange.GetLeft(); x <= validRange.GetRight(); x += COORDS_XY_STEP)
{
if (!LocationValid({ x, y }))
continue;
auto result = map_buy_land_rights_for_tile({ x, y }, isExecuting);
if (result->Error == GameActions::Status::Ok)
{
res->Cost += result->Cost;
}
}
}
if (isExecuting)
{
map_count_remaining_land_rights();
OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, centre);
}
return res;
}
GameActions::Result::Ptr LandSetRightsAction::map_buy_land_rights_for_tile(const CoordsXY& loc, bool isExecuting) const
{
SurfaceElement* surfaceElement = map_get_surface_element_at(loc);
if (surfaceElement == nullptr)
{
log_error("Could not find surface. x = %d, y = %d", loc.x, loc.y);
return MakeResult(GameActions::Status::InvalidParameters, STR_NONE, STR_NONE);
}
auto res = MakeResult();
switch (_setting)
{
case LandSetRightSetting::UnownLand:
if (isExecuting)
{
surfaceElement->SetOwnership(
surfaceElement->GetOwnership() & ~(OWNERSHIP_OWNED | OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED));
update_park_fences_around_tile(loc);
}
return res;
case LandSetRightSetting::UnownConstructionRights:
if (isExecuting)
{
surfaceElement->SetOwnership(surfaceElement->GetOwnership() & ~OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED);
uint16_t baseZ = surfaceElement->GetBaseZ();
map_invalidate_tile({ loc, baseZ, baseZ + 16 });
}
return res;
case LandSetRightSetting::SetForSale:
if (isExecuting)
{
surfaceElement->SetOwnership(surfaceElement->GetOwnership() | OWNERSHIP_AVAILABLE);
uint16_t baseZ = surfaceElement->GetBaseZ();
map_invalidate_tile({ loc, baseZ, baseZ + 16 });
}
return res;
case LandSetRightSetting::SetConstructionRightsForSale:
if (isExecuting)
{
surfaceElement->SetOwnership(surfaceElement->GetOwnership() | OWNERSHIP_CONSTRUCTION_RIGHTS_AVAILABLE);
uint16_t baseZ = surfaceElement->GetBaseZ();
map_invalidate_tile({ loc, baseZ, baseZ + 16 });
}
return res;
case LandSetRightSetting::SetOwnershipWithChecks:
{
if (_ownership == surfaceElement->GetOwnership())
{
return res;
}
TileElement* tileElement = map_get_first_element_at(loc);
do
{
if (tileElement == nullptr)
break;
if (tileElement->GetType() != TILE_ELEMENT_TYPE_ENTRANCE)
continue;
if (tileElement->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_PARK_ENTRANCE)
continue;
// Do not allow ownership of park entrance.
if (_ownership == OWNERSHIP_OWNED || _ownership == OWNERSHIP_AVAILABLE)
return res;
// Allow construction rights available / for sale on park entrances on surface.
// There is no need to check the height if _ownership is 0 (unowned and no rights available).
if (_ownership == OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED || _ownership == OWNERSHIP_CONSTRUCTION_RIGHTS_AVAILABLE)
{
if (tileElement->base_height - 3 > surfaceElement->base_height
|| tileElement->base_height < surfaceElement->base_height)
return res;
}
} while (!(tileElement++)->IsLastForTile());
res->Cost = gLandPrice;
if (isExecuting)
{
if (_ownership != OWNERSHIP_UNOWNED)
{
gPeepSpawns.erase(
std::remove_if(
gPeepSpawns.begin(), gPeepSpawns.end(),
[x = loc.x, y = loc.y](const auto& spawn) {
return floor2(spawn.x, 32) == x && floor2(spawn.y, 32) == y;
}),
gPeepSpawns.end());
}
surfaceElement->SetOwnership(_ownership);
update_park_fences_around_tile(loc);
gMapLandRightsUpdateSuccess = true;
}
return res;
}
default:
log_warning("Tried calling set land rights with an incorrect setting. setting = %u", _setting);
return MakeResult(GameActions::Status::InvalidParameters, STR_NONE, STR_NONE);
}
}

View File

@ -0,0 +1,60 @@
/*****************************************************************************
* 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
#include "GameAction.h"
enum class LandSetRightSetting : uint8_t
{
UnownLand,
UnownConstructionRights,
SetForSale,
SetConstructionRightsForSale,
SetOwnershipWithChecks,
Count
};
DEFINE_GAME_ACTION(LandSetRightsAction, GAME_COMMAND_SET_LAND_OWNERSHIP, GameActions::Result)
{
private:
MapRange _range;
LandSetRightSetting _setting{ LandSetRightSetting::Count };
uint8_t _ownership{};
public:
LandSetRightsAction() = default;
LandSetRightsAction(const MapRange& range, LandSetRightSetting setting, uint8_t ownership = 0)
: _range(range)
, _setting(setting)
, _ownership(ownership)
{
}
LandSetRightsAction(const CoordsXY& coord, LandSetRightSetting setting, uint8_t ownership = 0)
: _range(coord.x, coord.y, coord.x, coord.y)
, _setting(setting)
, _ownership(ownership)
{
}
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags() | GameActions::Flags::EditorOnly;
}
void Serialise(DataSerialiser & stream) override;
GameActions::Result::Ptr Query() const override;
GameActions::Result::Ptr Execute() const override;
private:
GameActions::Result::Ptr QueryExecute(bool isExecuting) const;
GameActions::Result::Ptr map_buy_land_rights_for_tile(const CoordsXY& loc, bool isExecuting) const;
};

View File

@ -1,236 +0,0 @@
/*****************************************************************************
* 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
#include "../Context.h"
#include "../OpenRCT2.h"
#include "../actions/LandSetHeightAction.hpp"
#include "../audio/audio.h"
#include "../interface/Window.h"
#include "../localisation/Localisation.h"
#include "../localisation/StringIds.h"
#include "../management/Finance.h"
#include "../ride/RideData.h"
#include "../util/Util.h"
#include "../windows/Intent.h"
#include "../world/Park.h"
#include "../world/Scenery.h"
#include "../world/Sprite.h"
#include "../world/Surface.h"
#include "GameAction.h"
enum class LandSetRightSetting : uint8_t
{
UnownLand,
UnownConstructionRights,
SetForSale,
SetConstructionRightsForSale,
SetOwnershipWithChecks,
Count
};
DEFINE_GAME_ACTION(LandSetRightsAction, GAME_COMMAND_SET_LAND_OWNERSHIP, GameActions::Result)
{
private:
MapRange _range;
LandSetRightSetting _setting{ LandSetRightSetting::Count };
uint8_t _ownership{};
public:
LandSetRightsAction() = default;
LandSetRightsAction(const MapRange& range, LandSetRightSetting setting, uint8_t ownership = 0)
: _range(range)
, _setting(setting)
, _ownership(ownership)
{
}
LandSetRightsAction(const CoordsXY& coord, LandSetRightSetting setting, uint8_t ownership = 0)
: _range(coord.x, coord.y, coord.x, coord.y)
, _setting(setting)
, _ownership(ownership)
{
}
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags() | GameActions::Flags::EditorOnly;
}
void Serialise(DataSerialiser & stream) override
{
GameAction::Serialise(stream);
stream << DS_TAG(_range) << DS_TAG(_setting) << DS_TAG(_ownership);
}
GameActions::Result::Ptr Query() const override
{
return QueryExecute(false);
}
GameActions::Result::Ptr Execute() const override
{
return QueryExecute(true);
}
private:
GameActions::Result::Ptr QueryExecute(bool isExecuting) const
{
auto res = MakeResult();
MapRange normRange = _range.Normalise();
// Keep big coordinates within map boundaries
auto aX = std::max<decltype(normRange.GetLeft())>(32, normRange.GetLeft());
auto bX = std::min<decltype(normRange.GetRight())>(gMapSizeMaxXY, normRange.GetRight());
auto aY = std::max<decltype(normRange.GetTop())>(32, normRange.GetTop());
auto bY = std::min<decltype(normRange.GetBottom())>(gMapSizeMaxXY, normRange.GetBottom());
MapRange validRange = MapRange{ aX, aY, bX, bY };
CoordsXYZ centre{ (validRange.GetLeft() + validRange.GetRight()) / 2 + 16,
(validRange.GetTop() + validRange.GetBottom()) / 2 + 16, 0 };
centre.z = tile_element_height(centre);
res->Position = centre;
res->Expenditure = ExpenditureType::LandPurchase;
if (!(gScreenFlags & SCREEN_FLAGS_EDITOR) && !gCheatsSandboxMode)
{
return MakeResult(GameActions::Status::NotInEditorMode, STR_NONE, STR_LAND_NOT_FOR_SALE);
}
// Game command modified to accept selection size
for (auto y = validRange.GetTop(); y <= validRange.GetBottom(); y += COORDS_XY_STEP)
{
for (auto x = validRange.GetLeft(); x <= validRange.GetRight(); x += COORDS_XY_STEP)
{
if (!LocationValid({ x, y }))
continue;
auto result = map_buy_land_rights_for_tile({ x, y }, isExecuting);
if (result->Error == GameActions::Status::Ok)
{
res->Cost += result->Cost;
}
}
}
if (isExecuting)
{
map_count_remaining_land_rights();
OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, centre);
}
return res;
}
GameActions::Result::Ptr map_buy_land_rights_for_tile(const CoordsXY& loc, bool isExecuting) const
{
SurfaceElement* surfaceElement = map_get_surface_element_at(loc);
if (surfaceElement == nullptr)
{
log_error("Could not find surface. x = %d, y = %d", loc.x, loc.y);
return MakeResult(GameActions::Status::InvalidParameters, STR_NONE, STR_NONE);
}
auto res = MakeResult();
switch (_setting)
{
case LandSetRightSetting::UnownLand:
if (isExecuting)
{
surfaceElement->SetOwnership(
surfaceElement->GetOwnership() & ~(OWNERSHIP_OWNED | OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED));
update_park_fences_around_tile(loc);
}
return res;
case LandSetRightSetting::UnownConstructionRights:
if (isExecuting)
{
surfaceElement->SetOwnership(surfaceElement->GetOwnership() & ~OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED);
uint16_t baseZ = surfaceElement->GetBaseZ();
map_invalidate_tile({ loc, baseZ, baseZ + 16 });
}
return res;
case LandSetRightSetting::SetForSale:
if (isExecuting)
{
surfaceElement->SetOwnership(surfaceElement->GetOwnership() | OWNERSHIP_AVAILABLE);
uint16_t baseZ = surfaceElement->GetBaseZ();
map_invalidate_tile({ loc, baseZ, baseZ + 16 });
}
return res;
case LandSetRightSetting::SetConstructionRightsForSale:
if (isExecuting)
{
surfaceElement->SetOwnership(surfaceElement->GetOwnership() | OWNERSHIP_CONSTRUCTION_RIGHTS_AVAILABLE);
uint16_t baseZ = surfaceElement->GetBaseZ();
map_invalidate_tile({ loc, baseZ, baseZ + 16 });
}
return res;
case LandSetRightSetting::SetOwnershipWithChecks:
{
if (_ownership == surfaceElement->GetOwnership())
{
return res;
}
TileElement* tileElement = map_get_first_element_at(loc);
do
{
if (tileElement == nullptr)
break;
if (tileElement->GetType() != TILE_ELEMENT_TYPE_ENTRANCE)
continue;
if (tileElement->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_PARK_ENTRANCE)
continue;
// Do not allow ownership of park entrance.
if (_ownership == OWNERSHIP_OWNED || _ownership == OWNERSHIP_AVAILABLE)
return res;
// Allow construction rights available / for sale on park entrances on surface.
// There is no need to check the height if _ownership is 0 (unowned and no rights available).
if (_ownership == OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED
|| _ownership == OWNERSHIP_CONSTRUCTION_RIGHTS_AVAILABLE)
{
if (tileElement->base_height - 3 > surfaceElement->base_height
|| tileElement->base_height < surfaceElement->base_height)
return res;
}
} while (!(tileElement++)->IsLastForTile());
res->Cost = gLandPrice;
if (isExecuting)
{
if (_ownership != OWNERSHIP_UNOWNED)
{
gPeepSpawns.erase(
std::remove_if(
gPeepSpawns.begin(), gPeepSpawns.end(),
[x = loc.x, y = loc.y](const auto& spawn) {
return floor2(spawn.x, 32) == x && floor2(spawn.y, 32) == y;
}),
gPeepSpawns.end());
}
surfaceElement->SetOwnership(_ownership);
update_park_fences_around_tile(loc);
gMapLandRightsUpdateSuccess = true;
}
return res;
}
default:
log_warning("Tried calling set land rights with an incorrect setting. setting = %u", _setting);
return MakeResult(GameActions::Status::InvalidParameters, STR_NONE, STR_NONE);
}
}
};

View File

@ -0,0 +1,635 @@
/*****************************************************************************
* 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.
*****************************************************************************/
#include "LandSmoothAction.h"
#include "../Context.h"
#include "../OpenRCT2.h"
#include "../actions/LandLowerAction.h"
#include "../actions/LandRaiseAction.h"
#include "../actions/LandSetHeightAction.h"
#include "../audio/audio.h"
#include "../interface/Window.h"
#include "../localisation/Localisation.h"
#include "../localisation/StringIds.h"
#include "../management/Finance.h"
#include "../ride/RideData.h"
#include "../windows/Intent.h"
#include "../world/Park.h"
#include "../world/Scenery.h"
#include "../world/Sprite.h"
#include "../world/Surface.h"
void LandSmoothAction::Serialise(DataSerialiser& stream)
{
GameAction::Serialise(stream);
stream << DS_TAG(_coords) << DS_TAG(_range) << DS_TAG(_selectionType) << DS_TAG(_isLowering);
}
GameActions::Result::Ptr LandSmoothAction::Query() const
{
return SmoothLand(false);
}
GameActions::Result::Ptr LandSmoothAction::Execute() const
{
return SmoothLand(true);
}
GameActions::Result::Ptr LandSmoothAction::SmoothLandTile(
int32_t direction, bool isExecuting, const CoordsXY& loc, SurfaceElement* surfaceElement) const
{
int32_t targetBaseZ = surfaceElement->base_height;
int32_t slope = surfaceElement->GetSlope();
if (_isLowering)
{
slope = tile_element_lower_styles[direction][slope];
if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT)
{
targetBaseZ -= 2;
slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
}
}
else
{
slope = tile_element_raise_styles[direction][slope];
if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT)
{
targetBaseZ += 2;
slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
}
}
auto landSetHeightAction = LandSetHeightAction(loc, targetBaseZ, slope);
landSetHeightAction.SetFlags(GetFlags());
auto res = isExecuting ? GameActions::ExecuteNested(&landSetHeightAction) : GameActions::QueryNested(&landSetHeightAction);
return res;
}
money32 LandSmoothAction::SmoothLandRowByEdge(
bool isExecuting, const CoordsXY& loc, int32_t expectedLandHeight1, int32_t expectedLandHeight2, int32_t stepX,
int32_t stepY, int32_t direction1, int32_t direction2, int32_t checkDirection1, int32_t checkDirection2) const
{
uint8_t shouldContinue = 0xF;
int32_t landChangePerTile = _isLowering ? 2 : -2;
money32 totalCost = 0;
// check if we need to start at all
if (!LocationValid(loc) || !LocationValid({ loc.x + stepX, loc.y + stepY }))
{
return 0;
}
auto surfaceElement = map_get_surface_element_at(loc);
auto nextSurfaceElement = map_get_surface_element_at(CoordsXY{ loc.x + stepX, loc.y + stepY });
if (surfaceElement == nullptr || nextSurfaceElement == nullptr)
{
return 0;
}
if (tile_element_get_corner_height(surfaceElement, checkDirection1) != expectedLandHeight1 + landChangePerTile)
{
shouldContinue &= ~0x1;
}
if (tile_element_get_corner_height(surfaceElement, checkDirection2) != expectedLandHeight2 + landChangePerTile)
{
shouldContinue &= ~0x2;
}
if (tile_element_get_corner_height(surfaceElement, checkDirection1)
!= tile_element_get_corner_height(nextSurfaceElement, direction1))
{
shouldContinue &= ~0x1;
}
if (tile_element_get_corner_height(surfaceElement, checkDirection2)
!= tile_element_get_corner_height(nextSurfaceElement, direction2))
{
shouldContinue &= ~0x2;
}
auto nextLoc = loc;
while ((shouldContinue & 0x3) != 0)
{
shouldContinue = ((shouldContinue << 2) | 0x3) & shouldContinue;
nextLoc.x += stepX;
nextLoc.y += stepY;
// check if we need to continue after raising the current tile
// this needs to be checked before the tile is changed
if (!LocationValid({ nextLoc.x + stepX, nextLoc.y + stepY }))
{
shouldContinue &= ~0x3;
}
else
{
surfaceElement = nextSurfaceElement;
nextSurfaceElement = map_get_surface_element_at(CoordsXY{ nextLoc.x + stepX, nextLoc.y + stepY });
if (nextSurfaceElement == nullptr)
{
shouldContinue &= ~0x3;
}
if (tile_element_get_corner_height(surfaceElement, direction1) + landChangePerTile
!= tile_element_get_corner_height(surfaceElement, checkDirection1))
{
shouldContinue &= ~0x1;
}
if (tile_element_get_corner_height(surfaceElement, direction2) + landChangePerTile
!= tile_element_get_corner_height(surfaceElement, checkDirection2))
{
shouldContinue &= ~0x2;
}
if ((shouldContinue & 0x1)
&& tile_element_get_corner_height(surfaceElement, checkDirection1)
!= tile_element_get_corner_height(nextSurfaceElement, direction1))
{
shouldContinue &= ~0x1;
}
if ((shouldContinue & 0x2)
&& tile_element_get_corner_height(surfaceElement, checkDirection2)
!= tile_element_get_corner_height(nextSurfaceElement, direction2))
{
shouldContinue &= ~0x2;
}
}
expectedLandHeight1 += landChangePerTile;
// change land of current tile
int32_t targetBaseZ = surfaceElement->base_height;
int32_t slope = surfaceElement->GetSlope();
int32_t oldSlope = slope;
if (_isLowering)
{
if (shouldContinue & 0x4)
{
slope = tile_element_lower_styles[direction1][slope];
if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT)
{
targetBaseZ -= 2;
slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
}
}
if ((shouldContinue & 0x8)
&& map_get_corner_height(surfaceElement->base_height, oldSlope, direction2)
== map_get_corner_height(targetBaseZ, slope, direction2))
{
slope = tile_element_lower_styles[direction2][slope];
if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT)
{
targetBaseZ -= 2;
slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
}
}
}
else
{
if (shouldContinue & 0x4)
{
slope = tile_element_raise_styles[direction1][slope];
if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT)
{
targetBaseZ += 2;
slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
}
}
if ((shouldContinue & 0x8)
&& map_get_corner_height(surfaceElement->base_height, oldSlope, direction2)
== map_get_corner_height(targetBaseZ, slope, direction2))
{
slope = tile_element_raise_styles[direction2][slope];
if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT)
{
targetBaseZ += 2;
slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
}
}
}
auto landSetHeightAction = LandSetHeightAction(nextLoc, targetBaseZ, slope);
landSetHeightAction.SetFlags(GetFlags());
auto res = isExecuting ? GameActions::ExecuteNested(&landSetHeightAction)
: GameActions::QueryNested(&landSetHeightAction);
if (res->Error == GameActions::Status::Ok)
{
totalCost += res->Cost;
}
}
return totalCost;
}
money32 LandSmoothAction::SmoothLandRowByCorner(
bool isExecuting, const CoordsXY& loc, int32_t expectedLandHeight, int32_t stepX, int32_t stepY, int32_t direction,
int32_t checkDirection) const
{
bool shouldContinue = true;
money32 totalCost = 0;
int32_t landChangePerTile;
if (stepX == 0 || stepY == 0)
{
landChangePerTile = _isLowering ? 2 : -2;
}
else
{
landChangePerTile = _isLowering ? 4 : -4;
}
// check if we need to start at all
if (!LocationValid(loc) || !LocationValid({ loc.x + stepX, loc.y + stepY }))
{
return 0;
}
auto surfaceElement = map_get_surface_element_at(loc);
auto nextSurfaceElement = map_get_surface_element_at(CoordsXY{ loc.x + stepX, loc.y + stepY });
if (surfaceElement == nullptr || nextSurfaceElement == nullptr)
{
return 0;
}
if (tile_element_get_corner_height(surfaceElement, checkDirection) != expectedLandHeight + (_isLowering ? 2 : -2))
{
return 0;
}
if (tile_element_get_corner_height(surfaceElement, checkDirection)
!= tile_element_get_corner_height(nextSurfaceElement, direction))
{
return 0;
}
auto nextLoc = loc;
while (shouldContinue)
{
nextLoc.x += stepX;
nextLoc.y += stepY;
// check if we need to continue after raising the current tile
// this needs to be checked before the tile is changed
if (!LocationValid({ nextLoc.x + stepX, nextLoc.y + stepY }))
{
shouldContinue = false;
}
else
{
surfaceElement = nextSurfaceElement;
nextSurfaceElement = map_get_surface_element_at(CoordsXY{ nextLoc.x + stepX, nextLoc.y + stepY });
if (nextSurfaceElement == nullptr)
{
shouldContinue = false;
}
if (tile_element_get_corner_height(surfaceElement, direction) + landChangePerTile
!= tile_element_get_corner_height(surfaceElement, checkDirection))
{
shouldContinue = false;
}
if (shouldContinue
&& tile_element_get_corner_height(surfaceElement, checkDirection)
!= tile_element_get_corner_height(nextSurfaceElement, direction))
{
shouldContinue = false;
}
}
if (stepX * stepY != 0)
{
totalCost += SmoothLandRowByCorner(
isExecuting, nextLoc, expectedLandHeight + (landChangePerTile / 2), 0, stepY, direction, checkDirection ^ 3);
totalCost += SmoothLandRowByCorner(
isExecuting, nextLoc, expectedLandHeight + (landChangePerTile / 2), stepX, 0, direction, checkDirection ^ 1);
}
expectedLandHeight += landChangePerTile;
// change land of current tile
auto result = SmoothLandTile(direction, isExecuting, nextLoc, surfaceElement);
if (result->Error == GameActions::Status::Ok)
{
totalCost += result->Cost;
}
}
return totalCost;
}
GameActions::Result::Ptr LandSmoothAction::SmoothLand(bool isExecuting) const
{
const bool raiseLand = !_isLowering;
const int32_t selectionType = _selectionType;
const int32_t heightOffset = raiseLand ? 2 : -2;
auto normRange = _range.Normalise();
// Cap bounds to map
auto l = std::max(normRange.GetLeft(), 32);
auto t = std::max(normRange.GetTop(), 32);
auto r = std::clamp(normRange.GetRight(), 0, MAXIMUM_TILE_START_XY);
auto b = std::clamp(normRange.GetBottom(), 0, MAXIMUM_TILE_START_XY);
auto validRange = MapRange{ l, t, r, b };
int32_t centreZ = tile_element_height(_coords);
auto res = MakeResult();
res->ErrorTitle = _ErrorTitles[_isLowering ? 0 : 1];
res->Expenditure = ExpenditureType::Landscaping;
res->Position = { _coords.x, _coords.y, centreZ };
// Do the smoothing
switch (selectionType)
{
case MAP_SELECT_TYPE_FULL:
{
uint8_t minHeight = heightOffset + map_get_lowest_land_height(validRange);
uint8_t maxHeight = heightOffset + map_get_highest_land_height(validRange);
// Smooth the 4 corners
{ // top-left
auto surfaceElement = map_get_surface_element_at(CoordsXY{ validRange.GetLeft(), validRange.GetTop() });
if (surfaceElement != nullptr)
{
int32_t z = std::clamp(
static_cast<uint8_t>(tile_element_get_corner_height(surfaceElement, 2)), minHeight, maxHeight);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, -32, 0, 2);
}
}
{ // bottom-left
auto surfaceElement = map_get_surface_element_at(CoordsXY{ validRange.GetLeft(), validRange.GetBottom() });
if (surfaceElement != nullptr)
{
int32_t z = std::clamp(
static_cast<uint8_t>(tile_element_get_corner_height(surfaceElement, 3)), minHeight, maxHeight);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetBottom() }, z, -32, 32, 1, 3);
}
}
{ // bottom-right
auto surfaceElement = map_get_surface_element_at(CoordsXY{ validRange.GetRight(), validRange.GetBottom() });
if (surfaceElement != nullptr)
{
int32_t z = std::clamp(
static_cast<uint8_t>(tile_element_get_corner_height(surfaceElement, 0)), minHeight, maxHeight);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetRight(), validRange.GetBottom() }, z, 32, 32, 2, 0);
}
}
{ // top-right
auto surfaceElement = map_get_surface_element_at(CoordsXY{ validRange.GetRight(), validRange.GetTop() });
if (surfaceElement != nullptr)
{
int32_t z = std::clamp(
static_cast<uint8_t>(tile_element_get_corner_height(surfaceElement, 1)), minHeight, maxHeight);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetRight(), validRange.GetTop() }, z, 32, -32, 3, 1);
}
}
// Smooth the edges
int32_t z1, z2;
for (int32_t y = validRange.GetTop(); y <= validRange.GetBottom(); y += COORDS_XY_STEP)
{
auto surfaceElement = map_get_surface_element_at(CoordsXY{ validRange.GetLeft(), y });
if (surfaceElement != nullptr)
{
z1 = std::clamp(
static_cast<uint8_t>(tile_element_get_corner_height(surfaceElement, 3)), minHeight, maxHeight);
z2 = std::clamp(
static_cast<uint8_t>(tile_element_get_corner_height(surfaceElement, 2)), minHeight, maxHeight);
res->Cost += SmoothLandRowByEdge(isExecuting, { validRange.GetLeft(), y }, z1, z2, -32, 0, 0, 1, 3, 2);
}
surfaceElement = map_get_surface_element_at(CoordsXY{ validRange.GetRight(), y });
if (surfaceElement != nullptr)
{
z1 = std::clamp(
static_cast<uint8_t>(tile_element_get_corner_height(surfaceElement, 1)), minHeight, maxHeight);
z2 = std::clamp(
static_cast<uint8_t>(tile_element_get_corner_height(surfaceElement, 0)), minHeight, maxHeight);
res->Cost += SmoothLandRowByEdge(isExecuting, { validRange.GetRight(), y }, z1, z2, 32, 0, 2, 3, 1, 0);
}
}
for (int32_t x = validRange.GetLeft(); x <= validRange.GetRight(); x += COORDS_XY_STEP)
{
auto surfaceElement = map_get_surface_element_at(CoordsXY{ x, validRange.GetTop() });
if (surfaceElement != nullptr)
{
z1 = std::clamp(
static_cast<uint8_t>(tile_element_get_corner_height(surfaceElement, 1)), minHeight, maxHeight);
z2 = std::clamp(
static_cast<uint8_t>(tile_element_get_corner_height(surfaceElement, 2)), minHeight, maxHeight);
res->Cost += SmoothLandRowByEdge(isExecuting, { x, validRange.GetTop() }, z1, z2, 0, -32, 0, 3, 1, 2);
}
surfaceElement = map_get_surface_element_at(CoordsXY{ x, validRange.GetBottom() });
if (surfaceElement != nullptr)
{
z1 = std::clamp(
static_cast<uint8_t>(tile_element_get_corner_height(surfaceElement, 0)), minHeight, maxHeight);
z2 = std::clamp(
static_cast<uint8_t>(tile_element_get_corner_height(surfaceElement, 3)), minHeight, maxHeight);
res->Cost += SmoothLandRowByEdge(isExecuting, { x, validRange.GetBottom() }, z1, z2, 0, 32, 1, 2, 0, 3);
}
}
break;
}
case MAP_SELECT_TYPE_CORNER_0:
case MAP_SELECT_TYPE_CORNER_1:
case MAP_SELECT_TYPE_CORNER_2:
case MAP_SELECT_TYPE_CORNER_3:
{
auto surfaceElement = map_get_surface_element_at(CoordsXY{ validRange.GetLeft(), validRange.GetTop() });
if (surfaceElement == nullptr)
break;
uint8_t newBaseZ = surfaceElement->base_height;
uint8_t newSlope = surfaceElement->GetSlope();
if (raiseLand)
{
newSlope = tile_element_raise_styles[selectionType][newSlope];
}
else
{
newSlope = tile_element_lower_styles[selectionType][newSlope];
}
if (newSlope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT)
{
newBaseZ += heightOffset;
newSlope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
}
// Smooth the corners
int32_t z = map_get_corner_height(newBaseZ, newSlope, 2);
res->Cost += SmoothLandRowByCorner(isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, -32, 0, 2);
z = map_get_corner_height(newBaseZ, newSlope, 0);
res->Cost += SmoothLandRowByCorner(isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 32, 32, 2, 0);
z = map_get_corner_height(newBaseZ, newSlope, 3);
res->Cost += SmoothLandRowByCorner(isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, 32, 1, 3);
z = map_get_corner_height(newBaseZ, newSlope, 1);
res->Cost += SmoothLandRowByCorner(isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 32, -32, 3, 1);
// Smooth the edges
switch (selectionType)
{
case MAP_SELECT_TYPE_CORNER_0:
z = map_get_corner_height(newBaseZ, newSlope, 0);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 32, 0, 3, 0);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 0, 32, 1, 0);
z = map_get_corner_height(newBaseZ, newSlope, 3);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, 0, 0, 3);
z = map_get_corner_height(newBaseZ, newSlope, 1);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 0, -32, 0, 1);
break;
case MAP_SELECT_TYPE_CORNER_1:
z = map_get_corner_height(newBaseZ, newSlope, 1);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 32, 0, 2, 1);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 0, -32, 0, 1);
z = map_get_corner_height(newBaseZ, newSlope, 2);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, 0, 1, 2);
z = map_get_corner_height(newBaseZ, newSlope, 0);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 0, 32, 1, 0);
break;
case MAP_SELECT_TYPE_CORNER_2:
z = map_get_corner_height(newBaseZ, newSlope, 2);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, 0, 1, 2);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 0, -32, 3, 2);
z = map_get_corner_height(newBaseZ, newSlope, 1);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 32, 0, 2, 1);
z = map_get_corner_height(newBaseZ, newSlope, 3);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 0, 32, 2, 3);
break;
case MAP_SELECT_TYPE_CORNER_3:
z = map_get_corner_height(newBaseZ, newSlope, 3);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, 0, 0, 3);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 0, 32, 2, 3);
z = map_get_corner_height(newBaseZ, newSlope, 0);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 32, 0, 3, 0);
z = map_get_corner_height(newBaseZ, newSlope, 2);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 0, -32, 3, 2);
break;
}
break;
}
case MAP_SELECT_TYPE_EDGE_0:
case MAP_SELECT_TYPE_EDGE_1:
case MAP_SELECT_TYPE_EDGE_2:
case MAP_SELECT_TYPE_EDGE_3:
{
// TODO: Handle smoothing by edge
// Get the two corners to raise
auto surfaceElement = map_get_surface_element_at(CoordsXY{ validRange.GetLeft(), validRange.GetTop() });
if (surfaceElement == nullptr)
break;
uint8_t newBaseZ = surfaceElement->base_height;
uint8_t oldSlope = surfaceElement->GetSlope();
uint8_t newSlope = oldSlope;
int32_t rowIndex = selectionType - (MAP_SELECT_TYPE_EDGE_0 - MAP_SELECT_TYPE_FULL - 1);
if (raiseLand)
{
newSlope = tile_element_raise_styles[rowIndex][oldSlope];
}
else
{
newSlope = tile_element_lower_styles[rowIndex][oldSlope];
}
const bool changeBaseHeight = newSlope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
if (changeBaseHeight)
{
newBaseZ += heightOffset;
newSlope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
}
const uint8_t edge = selectionType - MAP_SELECT_TYPE_EDGE_0;
// Table with corners for each edge selection. The first two are the selected corners, the latter
// two are the opposites
static constexpr uint8_t cornerIndices[][4] = {
{ 2, 3, 1, 0 }, // MAP_SELECT_TYPE_EDGE_0
{ 3, 0, 2, 1 }, // MAP_SELECT_TYPE_EDGE_1
{ 0, 1, 3, 2 }, // MAP_SELECT_TYPE_EDGE_2
{ 1, 2, 0, 3 }, // MAP_SELECT_TYPE_EDGE_3
};
// Big coordinate offsets for the neigbouring tile for the given edge selection
static constexpr CoordsXY stepOffsets[] = {
{ -32, 0 },
{ 0, 32 },
{ 32, 0 },
{ 0, -32 },
};
// Smooth higher and lower edges
uint8_t c1 = cornerIndices[edge][0];
uint8_t c2 = cornerIndices[edge][1];
uint8_t c3 = cornerIndices[edge][2];
uint8_t c4 = cornerIndices[edge][3];
uint8_t z1 = map_get_corner_height(newBaseZ, newSlope, c1);
uint8_t z2 = map_get_corner_height(newBaseZ, newSlope, c2);
uint8_t z3 = map_get_corner_height(newBaseZ, newSlope, c3);
uint8_t z4 = map_get_corner_height(newBaseZ, newSlope, c4);
// Smooth the edge at the top of the new slope
res->Cost += SmoothLandRowByEdge(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z1, z2, stepOffsets[edge].x, stepOffsets[edge].y,
c3, c4, c1, c2);
// Smooth the edge at the bottom of the new slope
res->Cost += SmoothLandRowByEdge(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z3, z4, -stepOffsets[edge].x, -stepOffsets[edge].y,
c1, c2, c3, c4);
// Smooth corners
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z1, -stepOffsets[edge].y, stepOffsets[edge].x, c2,
c1);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z2, stepOffsets[edge].y, -stepOffsets[edge].x, c1,
c2);
int32_t z = map_get_corner_height(newBaseZ, newSlope, 2);
res->Cost += SmoothLandRowByCorner(isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, -32, 0, 2);
z = map_get_corner_height(newBaseZ, newSlope, 0);
res->Cost += SmoothLandRowByCorner(isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 32, 32, 2, 0);
z = map_get_corner_height(newBaseZ, newSlope, 3);
res->Cost += SmoothLandRowByCorner(isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, 32, 1, 3);
z = map_get_corner_height(newBaseZ, newSlope, 1);
res->Cost += SmoothLandRowByCorner(isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 32, -32, 3, 1);
break;
}
default:
log_error("Invalid map selection %u", _selectionType);
return MakeResult(GameActions::Status::InvalidParameters, res->ErrorTitle.GetStringId());
} // switch selectionType
// Raise / lower the land tool selection area
GameActions::Result::Ptr result;
if (raiseLand)
{
auto raiseLandAction = LandRaiseAction({ _coords.x, _coords.y }, validRange, selectionType);
raiseLandAction.SetFlags(GetFlags());
result = isExecuting ? GameActions::ExecuteNested(&raiseLandAction) : GameActions::QueryNested(&raiseLandAction);
}
else
{
auto lowerLandAction = LandLowerAction({ _coords.x, _coords.y }, validRange, selectionType);
lowerLandAction.SetFlags(GetFlags());
result = isExecuting ? GameActions::ExecuteNested(&lowerLandAction) : GameActions::QueryNested(&lowerLandAction);
}
if (result->Error != GameActions::Status::Ok)
{
return result;
}
if (isExecuting)
{
OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, { _coords.x, _coords.y, centreZ });
}
res->Cost += result->Cost;
return res;
}

View File

@ -0,0 +1,53 @@
/*****************************************************************************
* 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
#include "GameAction.h"
DEFINE_GAME_ACTION(LandSmoothAction, GAME_COMMAND_EDIT_LAND_SMOOTH, GameActions::Result)
{
private:
CoordsXY _coords;
MapRange _range;
uint8_t _selectionType{};
bool _isLowering{};
constexpr static rct_string_id _ErrorTitles[] = { STR_CANT_LOWER_LAND_HERE, STR_CANT_RAISE_LAND_HERE };
public:
LandSmoothAction() = default;
LandSmoothAction(const CoordsXY& coords, MapRange range, uint8_t selectionType, bool isLowering)
: _coords(coords)
, _range(range)
, _selectionType(selectionType)
, _isLowering(isLowering)
{
}
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags();
}
void Serialise(DataSerialiser & stream) override;
GameActions::Result::Ptr Query() const override;
GameActions::Result::Ptr Execute() const override;
private:
GameActions::Result::Ptr SmoothLandTile(
int32_t direction, bool isExecuting, const CoordsXY& loc, SurfaceElement* surfaceElement) const;
money32 SmoothLandRowByEdge(
bool isExecuting, const CoordsXY& loc, int32_t expectedLandHeight1, int32_t expectedLandHeight2, int32_t stepX,
int32_t stepY, int32_t direction1, int32_t direction2, int32_t checkDirection1, int32_t checkDirection2) const;
money32 SmoothLandRowByCorner(
bool isExecuting, const CoordsXY& loc, int32_t expectedLandHeight, int32_t stepX, int32_t stepY, int32_t direction,
int32_t checkDirection) const;
GameActions::Result::Ptr SmoothLand(bool isExecuting) const;
};

View File

@ -1,672 +0,0 @@
/*****************************************************************************
* 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
#include "../Context.h"
#include "../OpenRCT2.h"
#include "../actions/LandLowerAction.hpp"
#include "../actions/LandRaiseAction.hpp"
#include "../actions/LandSetHeightAction.hpp"
#include "../audio/audio.h"
#include "../interface/Window.h"
#include "../localisation/Localisation.h"
#include "../localisation/StringIds.h"
#include "../management/Finance.h"
#include "../ride/RideData.h"
#include "../windows/Intent.h"
#include "../world/Park.h"
#include "../world/Scenery.h"
#include "../world/Sprite.h"
#include "../world/Surface.h"
#include "GameAction.h"
DEFINE_GAME_ACTION(LandSmoothAction, GAME_COMMAND_EDIT_LAND_SMOOTH, GameActions::Result)
{
private:
CoordsXY _coords;
MapRange _range;
uint8_t _selectionType{};
bool _isLowering{};
constexpr static rct_string_id _ErrorTitles[] = { STR_CANT_LOWER_LAND_HERE, STR_CANT_RAISE_LAND_HERE };
public:
LandSmoothAction() = default;
LandSmoothAction(const CoordsXY& coords, MapRange range, uint8_t selectionType, bool isLowering)
: _coords(coords)
, _range(range)
, _selectionType(selectionType)
, _isLowering(isLowering)
{
}
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags();
}
void Serialise(DataSerialiser & stream) override
{
GameAction::Serialise(stream);
stream << DS_TAG(_coords) << DS_TAG(_range) << DS_TAG(_selectionType) << DS_TAG(_isLowering);
}
GameActions::Result::Ptr Query() const override
{
return SmoothLand(false);
}
GameActions::Result::Ptr Execute() const override
{
return SmoothLand(true);
}
private:
GameActions::Result::Ptr SmoothLandTile(
int32_t direction, bool isExecuting, const CoordsXY& loc, SurfaceElement* surfaceElement) const
{
int32_t targetBaseZ = surfaceElement->base_height;
int32_t slope = surfaceElement->GetSlope();
if (_isLowering)
{
slope = tile_element_lower_styles[direction][slope];
if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT)
{
targetBaseZ -= 2;
slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
}
}
else
{
slope = tile_element_raise_styles[direction][slope];
if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT)
{
targetBaseZ += 2;
slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
}
}
auto landSetHeightAction = LandSetHeightAction(loc, targetBaseZ, slope);
landSetHeightAction.SetFlags(GetFlags());
auto res = isExecuting ? GameActions::ExecuteNested(&landSetHeightAction)
: GameActions::QueryNested(&landSetHeightAction);
return res;
}
money32 SmoothLandRowByEdge(
bool isExecuting, const CoordsXY& loc, int32_t expectedLandHeight1, int32_t expectedLandHeight2, int32_t stepX,
int32_t stepY, int32_t direction1, int32_t direction2, int32_t checkDirection1, int32_t checkDirection2) const
{
uint8_t shouldContinue = 0xF;
int32_t landChangePerTile = _isLowering ? 2 : -2;
money32 totalCost = 0;
// check if we need to start at all
if (!LocationValid(loc) || !LocationValid({ loc.x + stepX, loc.y + stepY }))
{
return 0;
}
auto surfaceElement = map_get_surface_element_at(loc);
auto nextSurfaceElement = map_get_surface_element_at(CoordsXY{ loc.x + stepX, loc.y + stepY });
if (surfaceElement == nullptr || nextSurfaceElement == nullptr)
{
return 0;
}
if (tile_element_get_corner_height(surfaceElement, checkDirection1) != expectedLandHeight1 + landChangePerTile)
{
shouldContinue &= ~0x1;
}
if (tile_element_get_corner_height(surfaceElement, checkDirection2) != expectedLandHeight2 + landChangePerTile)
{
shouldContinue &= ~0x2;
}
if (tile_element_get_corner_height(surfaceElement, checkDirection1)
!= tile_element_get_corner_height(nextSurfaceElement, direction1))
{
shouldContinue &= ~0x1;
}
if (tile_element_get_corner_height(surfaceElement, checkDirection2)
!= tile_element_get_corner_height(nextSurfaceElement, direction2))
{
shouldContinue &= ~0x2;
}
auto nextLoc = loc;
while ((shouldContinue & 0x3) != 0)
{
shouldContinue = ((shouldContinue << 2) | 0x3) & shouldContinue;
nextLoc.x += stepX;
nextLoc.y += stepY;
// check if we need to continue after raising the current tile
// this needs to be checked before the tile is changed
if (!LocationValid({ nextLoc.x + stepX, nextLoc.y + stepY }))
{
shouldContinue &= ~0x3;
}
else
{
surfaceElement = nextSurfaceElement;
nextSurfaceElement = map_get_surface_element_at(CoordsXY{ nextLoc.x + stepX, nextLoc.y + stepY });
if (nextSurfaceElement == nullptr)
{
shouldContinue &= ~0x3;
}
if (tile_element_get_corner_height(surfaceElement, direction1) + landChangePerTile
!= tile_element_get_corner_height(surfaceElement, checkDirection1))
{
shouldContinue &= ~0x1;
}
if (tile_element_get_corner_height(surfaceElement, direction2) + landChangePerTile
!= tile_element_get_corner_height(surfaceElement, checkDirection2))
{
shouldContinue &= ~0x2;
}
if ((shouldContinue & 0x1)
&& tile_element_get_corner_height(surfaceElement, checkDirection1)
!= tile_element_get_corner_height(nextSurfaceElement, direction1))
{
shouldContinue &= ~0x1;
}
if ((shouldContinue & 0x2)
&& tile_element_get_corner_height(surfaceElement, checkDirection2)
!= tile_element_get_corner_height(nextSurfaceElement, direction2))
{
shouldContinue &= ~0x2;
}
}
expectedLandHeight1 += landChangePerTile;
// change land of current tile
int32_t targetBaseZ = surfaceElement->base_height;
int32_t slope = surfaceElement->GetSlope();
int32_t oldSlope = slope;
if (_isLowering)
{
if (shouldContinue & 0x4)
{
slope = tile_element_lower_styles[direction1][slope];
if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT)
{
targetBaseZ -= 2;
slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
}
}
if ((shouldContinue & 0x8)
&& map_get_corner_height(surfaceElement->base_height, oldSlope, direction2)
== map_get_corner_height(targetBaseZ, slope, direction2))
{
slope = tile_element_lower_styles[direction2][slope];
if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT)
{
targetBaseZ -= 2;
slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
}
}
}
else
{
if (shouldContinue & 0x4)
{
slope = tile_element_raise_styles[direction1][slope];
if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT)
{
targetBaseZ += 2;
slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
}
}
if ((shouldContinue & 0x8)
&& map_get_corner_height(surfaceElement->base_height, oldSlope, direction2)
== map_get_corner_height(targetBaseZ, slope, direction2))
{
slope = tile_element_raise_styles[direction2][slope];
if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT)
{
targetBaseZ += 2;
slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
}
}
}
auto landSetHeightAction = LandSetHeightAction(nextLoc, targetBaseZ, slope);
landSetHeightAction.SetFlags(GetFlags());
auto res = isExecuting ? GameActions::ExecuteNested(&landSetHeightAction)
: GameActions::QueryNested(&landSetHeightAction);
if (res->Error == GameActions::Status::Ok)
{
totalCost += res->Cost;
}
}
return totalCost;
}
money32 SmoothLandRowByCorner(
bool isExecuting, const CoordsXY& loc, int32_t expectedLandHeight, int32_t stepX, int32_t stepY, int32_t direction,
int32_t checkDirection) const
{
bool shouldContinue = true;
money32 totalCost = 0;
int32_t landChangePerTile;
if (stepX == 0 || stepY == 0)
{
landChangePerTile = _isLowering ? 2 : -2;
}
else
{
landChangePerTile = _isLowering ? 4 : -4;
}
// check if we need to start at all
if (!LocationValid(loc) || !LocationValid({ loc.x + stepX, loc.y + stepY }))
{
return 0;
}
auto surfaceElement = map_get_surface_element_at(loc);
auto nextSurfaceElement = map_get_surface_element_at(CoordsXY{ loc.x + stepX, loc.y + stepY });
if (surfaceElement == nullptr || nextSurfaceElement == nullptr)
{
return 0;
}
if (tile_element_get_corner_height(surfaceElement, checkDirection) != expectedLandHeight + (_isLowering ? 2 : -2))
{
return 0;
}
if (tile_element_get_corner_height(surfaceElement, checkDirection)
!= tile_element_get_corner_height(nextSurfaceElement, direction))
{
return 0;
}
auto nextLoc = loc;
while (shouldContinue)
{
nextLoc.x += stepX;
nextLoc.y += stepY;
// check if we need to continue after raising the current tile
// this needs to be checked before the tile is changed
if (!LocationValid({ nextLoc.x + stepX, nextLoc.y + stepY }))
{
shouldContinue = false;
}
else
{
surfaceElement = nextSurfaceElement;
nextSurfaceElement = map_get_surface_element_at(CoordsXY{ nextLoc.x + stepX, nextLoc.y + stepY });
if (nextSurfaceElement == nullptr)
{
shouldContinue = false;
}
if (tile_element_get_corner_height(surfaceElement, direction) + landChangePerTile
!= tile_element_get_corner_height(surfaceElement, checkDirection))
{
shouldContinue = false;
}
if (shouldContinue
&& tile_element_get_corner_height(surfaceElement, checkDirection)
!= tile_element_get_corner_height(nextSurfaceElement, direction))
{
shouldContinue = false;
}
}
if (stepX * stepY != 0)
{
totalCost += SmoothLandRowByCorner(
isExecuting, nextLoc, expectedLandHeight + (landChangePerTile / 2), 0, stepY, direction,
checkDirection ^ 3);
totalCost += SmoothLandRowByCorner(
isExecuting, nextLoc, expectedLandHeight + (landChangePerTile / 2), stepX, 0, direction,
checkDirection ^ 1);
}
expectedLandHeight += landChangePerTile;
// change land of current tile
auto result = SmoothLandTile(direction, isExecuting, nextLoc, surfaceElement);
if (result->Error == GameActions::Status::Ok)
{
totalCost += result->Cost;
}
}
return totalCost;
}
GameActions::Result::Ptr SmoothLand(bool isExecuting) const
{
const bool raiseLand = !_isLowering;
const int32_t selectionType = _selectionType;
const int32_t heightOffset = raiseLand ? 2 : -2;
auto normRange = _range.Normalise();
// Cap bounds to map
auto l = std::max(normRange.GetLeft(), 32);
auto t = std::max(normRange.GetTop(), 32);
auto r = std::clamp(normRange.GetRight(), 0, MAXIMUM_TILE_START_XY);
auto b = std::clamp(normRange.GetBottom(), 0, MAXIMUM_TILE_START_XY);
auto validRange = MapRange{ l, t, r, b };
int32_t centreZ = tile_element_height(_coords);
auto res = MakeResult();
res->ErrorTitle = _ErrorTitles[_isLowering ? 0 : 1];
res->Expenditure = ExpenditureType::Landscaping;
res->Position = { _coords.x, _coords.y, centreZ };
// Do the smoothing
switch (selectionType)
{
case MAP_SELECT_TYPE_FULL:
{
uint8_t minHeight = heightOffset + map_get_lowest_land_height(validRange);
uint8_t maxHeight = heightOffset + map_get_highest_land_height(validRange);
// Smooth the 4 corners
{ // top-left
auto surfaceElement = map_get_surface_element_at(CoordsXY{ validRange.GetLeft(), validRange.GetTop() });
if (surfaceElement != nullptr)
{
int32_t z = std::clamp(
static_cast<uint8_t>(tile_element_get_corner_height(surfaceElement, 2)), minHeight, maxHeight);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, -32, 0, 2);
}
}
{ // bottom-left
auto surfaceElement = map_get_surface_element_at(CoordsXY{ validRange.GetLeft(), validRange.GetBottom() });
if (surfaceElement != nullptr)
{
int32_t z = std::clamp(
static_cast<uint8_t>(tile_element_get_corner_height(surfaceElement, 3)), minHeight, maxHeight);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetBottom() }, z, -32, 32, 1, 3);
}
}
{ // bottom-right
auto surfaceElement = map_get_surface_element_at(CoordsXY{ validRange.GetRight(), validRange.GetBottom() });
if (surfaceElement != nullptr)
{
int32_t z = std::clamp(
static_cast<uint8_t>(tile_element_get_corner_height(surfaceElement, 0)), minHeight, maxHeight);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetRight(), validRange.GetBottom() }, z, 32, 32, 2, 0);
}
}
{ // top-right
auto surfaceElement = map_get_surface_element_at(CoordsXY{ validRange.GetRight(), validRange.GetTop() });
if (surfaceElement != nullptr)
{
int32_t z = std::clamp(
static_cast<uint8_t>(tile_element_get_corner_height(surfaceElement, 1)), minHeight, maxHeight);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetRight(), validRange.GetTop() }, z, 32, -32, 3, 1);
}
}
// Smooth the edges
int32_t z1, z2;
for (int32_t y = validRange.GetTop(); y <= validRange.GetBottom(); y += COORDS_XY_STEP)
{
auto surfaceElement = map_get_surface_element_at(CoordsXY{ validRange.GetLeft(), y });
if (surfaceElement != nullptr)
{
z1 = std::clamp(
static_cast<uint8_t>(tile_element_get_corner_height(surfaceElement, 3)), minHeight, maxHeight);
z2 = std::clamp(
static_cast<uint8_t>(tile_element_get_corner_height(surfaceElement, 2)), minHeight, maxHeight);
res->Cost += SmoothLandRowByEdge(isExecuting, { validRange.GetLeft(), y }, z1, z2, -32, 0, 0, 1, 3, 2);
}
surfaceElement = map_get_surface_element_at(CoordsXY{ validRange.GetRight(), y });
if (surfaceElement != nullptr)
{
z1 = std::clamp(
static_cast<uint8_t>(tile_element_get_corner_height(surfaceElement, 1)), minHeight, maxHeight);
z2 = std::clamp(
static_cast<uint8_t>(tile_element_get_corner_height(surfaceElement, 0)), minHeight, maxHeight);
res->Cost += SmoothLandRowByEdge(isExecuting, { validRange.GetRight(), y }, z1, z2, 32, 0, 2, 3, 1, 0);
}
}
for (int32_t x = validRange.GetLeft(); x <= validRange.GetRight(); x += COORDS_XY_STEP)
{
auto surfaceElement = map_get_surface_element_at(CoordsXY{ x, validRange.GetTop() });
if (surfaceElement != nullptr)
{
z1 = std::clamp(
static_cast<uint8_t>(tile_element_get_corner_height(surfaceElement, 1)), minHeight, maxHeight);
z2 = std::clamp(
static_cast<uint8_t>(tile_element_get_corner_height(surfaceElement, 2)), minHeight, maxHeight);
res->Cost += SmoothLandRowByEdge(isExecuting, { x, validRange.GetTop() }, z1, z2, 0, -32, 0, 3, 1, 2);
}
surfaceElement = map_get_surface_element_at(CoordsXY{ x, validRange.GetBottom() });
if (surfaceElement != nullptr)
{
z1 = std::clamp(
static_cast<uint8_t>(tile_element_get_corner_height(surfaceElement, 0)), minHeight, maxHeight);
z2 = std::clamp(
static_cast<uint8_t>(tile_element_get_corner_height(surfaceElement, 3)), minHeight, maxHeight);
res->Cost += SmoothLandRowByEdge(isExecuting, { x, validRange.GetBottom() }, z1, z2, 0, 32, 1, 2, 0, 3);
}
}
break;
}
case MAP_SELECT_TYPE_CORNER_0:
case MAP_SELECT_TYPE_CORNER_1:
case MAP_SELECT_TYPE_CORNER_2:
case MAP_SELECT_TYPE_CORNER_3:
{
auto surfaceElement = map_get_surface_element_at(CoordsXY{ validRange.GetLeft(), validRange.GetTop() });
if (surfaceElement == nullptr)
break;
uint8_t newBaseZ = surfaceElement->base_height;
uint8_t newSlope = surfaceElement->GetSlope();
if (raiseLand)
{
newSlope = tile_element_raise_styles[selectionType][newSlope];
}
else
{
newSlope = tile_element_lower_styles[selectionType][newSlope];
}
if (newSlope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT)
{
newBaseZ += heightOffset;
newSlope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
}
// Smooth the corners
int32_t z = map_get_corner_height(newBaseZ, newSlope, 2);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, -32, 0, 2);
z = map_get_corner_height(newBaseZ, newSlope, 0);
res->Cost += SmoothLandRowByCorner(isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 32, 32, 2, 0);
z = map_get_corner_height(newBaseZ, newSlope, 3);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, 32, 1, 3);
z = map_get_corner_height(newBaseZ, newSlope, 1);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 32, -32, 3, 1);
// Smooth the edges
switch (selectionType)
{
case MAP_SELECT_TYPE_CORNER_0:
z = map_get_corner_height(newBaseZ, newSlope, 0);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 32, 0, 3, 0);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 0, 32, 1, 0);
z = map_get_corner_height(newBaseZ, newSlope, 3);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, 0, 0, 3);
z = map_get_corner_height(newBaseZ, newSlope, 1);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 0, -32, 0, 1);
break;
case MAP_SELECT_TYPE_CORNER_1:
z = map_get_corner_height(newBaseZ, newSlope, 1);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 32, 0, 2, 1);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 0, -32, 0, 1);
z = map_get_corner_height(newBaseZ, newSlope, 2);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, 0, 1, 2);
z = map_get_corner_height(newBaseZ, newSlope, 0);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 0, 32, 1, 0);
break;
case MAP_SELECT_TYPE_CORNER_2:
z = map_get_corner_height(newBaseZ, newSlope, 2);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, 0, 1, 2);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 0, -32, 3, 2);
z = map_get_corner_height(newBaseZ, newSlope, 1);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 32, 0, 2, 1);
z = map_get_corner_height(newBaseZ, newSlope, 3);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 0, 32, 2, 3);
break;
case MAP_SELECT_TYPE_CORNER_3:
z = map_get_corner_height(newBaseZ, newSlope, 3);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, 0, 0, 3);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 0, 32, 2, 3);
z = map_get_corner_height(newBaseZ, newSlope, 0);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 32, 0, 3, 0);
z = map_get_corner_height(newBaseZ, newSlope, 2);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 0, -32, 3, 2);
break;
}
break;
}
case MAP_SELECT_TYPE_EDGE_0:
case MAP_SELECT_TYPE_EDGE_1:
case MAP_SELECT_TYPE_EDGE_2:
case MAP_SELECT_TYPE_EDGE_3:
{
// TODO: Handle smoothing by edge
// Get the two corners to raise
auto surfaceElement = map_get_surface_element_at(CoordsXY{ validRange.GetLeft(), validRange.GetTop() });
if (surfaceElement == nullptr)
break;
uint8_t newBaseZ = surfaceElement->base_height;
uint8_t oldSlope = surfaceElement->GetSlope();
uint8_t newSlope = oldSlope;
int32_t rowIndex = selectionType - (MAP_SELECT_TYPE_EDGE_0 - MAP_SELECT_TYPE_FULL - 1);
if (raiseLand)
{
newSlope = tile_element_raise_styles[rowIndex][oldSlope];
}
else
{
newSlope = tile_element_lower_styles[rowIndex][oldSlope];
}
const bool changeBaseHeight = newSlope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
if (changeBaseHeight)
{
newBaseZ += heightOffset;
newSlope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT;
}
const uint8_t edge = selectionType - MAP_SELECT_TYPE_EDGE_0;
// Table with corners for each edge selection. The first two are the selected corners, the latter
// two are the opposites
static constexpr uint8_t cornerIndices[][4] = {
{ 2, 3, 1, 0 }, // MAP_SELECT_TYPE_EDGE_0
{ 3, 0, 2, 1 }, // MAP_SELECT_TYPE_EDGE_1
{ 0, 1, 3, 2 }, // MAP_SELECT_TYPE_EDGE_2
{ 1, 2, 0, 3 }, // MAP_SELECT_TYPE_EDGE_3
};
// Big coordinate offsets for the neigbouring tile for the given edge selection
static constexpr CoordsXY stepOffsets[] = {
{ -32, 0 },
{ 0, 32 },
{ 32, 0 },
{ 0, -32 },
};
// Smooth higher and lower edges
uint8_t c1 = cornerIndices[edge][0];
uint8_t c2 = cornerIndices[edge][1];
uint8_t c3 = cornerIndices[edge][2];
uint8_t c4 = cornerIndices[edge][3];
uint8_t z1 = map_get_corner_height(newBaseZ, newSlope, c1);
uint8_t z2 = map_get_corner_height(newBaseZ, newSlope, c2);
uint8_t z3 = map_get_corner_height(newBaseZ, newSlope, c3);
uint8_t z4 = map_get_corner_height(newBaseZ, newSlope, c4);
// Smooth the edge at the top of the new slope
res->Cost += SmoothLandRowByEdge(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z1, z2, stepOffsets[edge].x,
stepOffsets[edge].y, c3, c4, c1, c2);
// Smooth the edge at the bottom of the new slope
res->Cost += SmoothLandRowByEdge(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z3, z4, -stepOffsets[edge].x,
-stepOffsets[edge].y, c1, c2, c3, c4);
// Smooth corners
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z1, -stepOffsets[edge].y, stepOffsets[edge].x,
c2, c1);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z2, stepOffsets[edge].y, -stepOffsets[edge].x,
c1, c2);
int32_t z = map_get_corner_height(newBaseZ, newSlope, 2);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, -32, 0, 2);
z = map_get_corner_height(newBaseZ, newSlope, 0);
res->Cost += SmoothLandRowByCorner(isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 32, 32, 2, 0);
z = map_get_corner_height(newBaseZ, newSlope, 3);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, -32, 32, 1, 3);
z = map_get_corner_height(newBaseZ, newSlope, 1);
res->Cost += SmoothLandRowByCorner(
isExecuting, { validRange.GetLeft(), validRange.GetTop() }, z, 32, -32, 3, 1);
break;
}
default:
log_error("Invalid map selection %u", _selectionType);
return MakeResult(GameActions::Status::InvalidParameters, res->ErrorTitle.GetStringId());
} // switch selectionType
// Raise / lower the land tool selection area
GameActions::Result::Ptr result;
if (raiseLand)
{
auto raiseLandAction = LandRaiseAction({ _coords.x, _coords.y }, validRange, selectionType);
raiseLandAction.SetFlags(GetFlags());
result = isExecuting ? GameActions::ExecuteNested(&raiseLandAction) : GameActions::QueryNested(&raiseLandAction);
}
else
{
auto lowerLandAction = LandLowerAction({ _coords.x, _coords.y }, validRange, selectionType);
lowerLandAction.SetFlags(GetFlags());
result = isExecuting ? GameActions::ExecuteNested(&lowerLandAction) : GameActions::QueryNested(&lowerLandAction);
}
if (result->Error != GameActions::Status::Ok)
{
return result;
}
if (isExecuting)
{
OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::PlaceItem, { _coords.x, _coords.y, centreZ });
}
res->Cost += result->Cost;
return res;
}
};

View File

@ -0,0 +1,359 @@
/*****************************************************************************
* 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.
*****************************************************************************/
#include "LargeSceneryPlaceAction.h"
#include "../OpenRCT2.h"
#include "../management/Finance.h"
#include "../object/ObjectLimits.h"
#include "../ride/Ride.h"
#include "../world/Banner.h"
#include "../world/MapAnimation.h"
#include "../world/Surface.h"
void LargeSceneryPlaceAction::AcceptParameters(GameActionParameterVisitor& visitor)
{
visitor.Visit(_loc);
visitor.Visit("object", _sceneryType);
visitor.Visit("primaryColour", _primaryColour);
visitor.Visit("secondaryColour", _secondaryColour);
rct_scenery_entry* sceneryEntry = get_large_scenery_entry(_sceneryType);
if (sceneryEntry != nullptr)
{
if (sceneryEntry->large_scenery.scrolling_mode != SCROLLING_MODE_NONE)
{
_bannerId = create_new_banner(0);
}
}
}
void LargeSceneryPlaceAction::Serialise(DataSerialiser& stream)
{
GameAction::Serialise(stream);
stream << DS_TAG(_loc) << DS_TAG(_sceneryType) << DS_TAG(_primaryColour) << DS_TAG(_secondaryColour) << DS_TAG(_bannerId);
}
GameActions::Result::Ptr LargeSceneryPlaceAction::Query() const
{
auto res = std::make_unique<LargeSceneryPlaceActionResult>();
res->ErrorTitle = STR_CANT_POSITION_THIS_HERE;
res->Expenditure = ExpenditureType::Landscaping;
int16_t surfaceHeight = tile_element_height(_loc);
res->Position.x = _loc.x + 16;
res->Position.y = _loc.y + 16;
res->Position.z = surfaceHeight;
res->GroundFlags = 0;
money32 supportsCost = 0;
if (_primaryColour > TILE_ELEMENT_COLOUR_MASK || _secondaryColour > TILE_ELEMENT_COLOUR_MASK)
{
log_error(
"Invalid game command for scenery placement, primaryColour = %u, secondaryColour = %u", _primaryColour,
_secondaryColour);
return std::make_unique<LargeSceneryPlaceActionResult>(GameActions::Status::InvalidParameters);
}
if (_sceneryType >= MAX_LARGE_SCENERY_OBJECTS)
{
log_error("Invalid game command for scenery placement, sceneryType = %u", _sceneryType);
return std::make_unique<LargeSceneryPlaceActionResult>(GameActions::Status::InvalidParameters);
}
rct_scenery_entry* sceneryEntry = get_large_scenery_entry(_sceneryType);
if (sceneryEntry == nullptr)
{
log_error("Invalid game command for scenery placement, sceneryType = %u", _sceneryType);
return std::make_unique<LargeSceneryPlaceActionResult>(GameActions::Status::InvalidParameters);
}
uint32_t totalNumTiles = GetTotalNumTiles(sceneryEntry->large_scenery.tiles);
int16_t maxHeight = GetMaxSurfaceHeight(sceneryEntry->large_scenery.tiles);
if (_loc.z != 0)
{
maxHeight = _loc.z;
}
res->Position.z = maxHeight;
if (sceneryEntry->large_scenery.scrolling_mode != SCROLLING_MODE_NONE)
{
if (_bannerId == BANNER_INDEX_NULL)
{
log_error("Banner Index not specified.");
return MakeResult(GameActions::Status::InvalidParameters, STR_TOO_MANY_BANNERS_IN_GAME);
}
auto banner = GetBanner(_bannerId);
if (!banner->IsNull())
{
log_error("No free banners available");
return std::make_unique<LargeSceneryPlaceActionResult>(GameActions::Status::NoFreeElements);
}
}
if (!map_check_free_elements_and_reorganise(totalNumTiles))
{
log_error("No free map elements available");
return std::make_unique<LargeSceneryPlaceActionResult>(GameActions::Status::NoFreeElements);
}
uint8_t tileNum = 0;
for (rct_large_scenery_tile* tile = sceneryEntry->large_scenery.tiles; tile->x_offset != -1; tile++, tileNum++)
{
auto curTile = CoordsXY{ tile->x_offset, tile->y_offset }.Rotate(_loc.direction);
curTile.x += _loc.x;
curTile.y += _loc.y;
int32_t zLow = tile->z_offset + maxHeight;
int32_t zHigh = tile->z_clearance + zLow;
QuarterTile quarterTile = QuarterTile{ static_cast<uint8_t>(tile->flags >> 12), 0 }.Rotate(_loc.direction);
if (!map_can_construct_with_clear_at(
{ curTile, zLow, zHigh }, &map_place_scenery_clear_func, quarterTile, GetFlags(), &supportsCost,
CREATE_CROSSING_MODE_NONE))
{
return std::make_unique<LargeSceneryPlaceActionResult>(
GameActions::Status::NoClearance, gGameCommandErrorText, gCommonFormatArgs);
}
int32_t tempSceneryGroundFlags = gMapGroundFlags & (ELEMENT_IS_ABOVE_GROUND | ELEMENT_IS_UNDERGROUND);
if (!gCheatsDisableClearanceChecks)
{
if ((gMapGroundFlags & ELEMENT_IS_UNDERWATER) || (gMapGroundFlags & ELEMENT_IS_UNDERGROUND))
{
return std::make_unique<LargeSceneryPlaceActionResult>(
GameActions::Status::Disallowed, STR_CANT_BUILD_THIS_UNDERWATER);
}
if (res->GroundFlags && !(res->GroundFlags & tempSceneryGroundFlags))
{
return std::make_unique<LargeSceneryPlaceActionResult>(
GameActions::Status::Disallowed, STR_CANT_BUILD_PARTLY_ABOVE_AND_PARTLY_BELOW_GROUND);
}
}
res->GroundFlags = tempSceneryGroundFlags;
if (!LocationValid(curTile) || curTile.x >= gMapSizeUnits || curTile.y >= gMapSizeUnits)
{
return std::make_unique<LargeSceneryPlaceActionResult>(GameActions::Status::Disallowed, STR_OFF_EDGE_OF_MAP);
}
if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !map_is_location_owned({ curTile, zLow }) && !gCheatsSandboxMode)
{
return std::make_unique<LargeSceneryPlaceActionResult>(GameActions::Status::Disallowed, STR_LAND_NOT_OWNED_BY_PARK);
}
}
// Force ride construction to recheck area
_currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_RECHECK;
res->Cost = (sceneryEntry->large_scenery.price * 10) + supportsCost;
return res;
}
GameActions::Result::Ptr LargeSceneryPlaceAction::Execute() const
{
auto res = std::make_unique<LargeSceneryPlaceActionResult>();
res->ErrorTitle = STR_CANT_POSITION_THIS_HERE;
res->Expenditure = ExpenditureType::Landscaping;
int16_t surfaceHeight = tile_element_height(_loc);
res->Position.x = _loc.x + 16;
res->Position.y = _loc.y + 16;
res->Position.z = surfaceHeight;
res->GroundFlags = 0;
money32 supportsCost = 0;
rct_scenery_entry* sceneryEntry = get_large_scenery_entry(_sceneryType);
if (sceneryEntry == nullptr)
{
log_error("Invalid game command for scenery placement, sceneryType = %u", _sceneryType);
return std::make_unique<LargeSceneryPlaceActionResult>(GameActions::Status::InvalidParameters);
}
if (sceneryEntry->large_scenery.tiles == nullptr)
{
log_error("Invalid large scenery object, sceneryType = %u", _sceneryType);
return std::make_unique<LargeSceneryPlaceActionResult>(GameActions::Status::InvalidParameters);
}
uint32_t totalNumTiles = GetTotalNumTiles(sceneryEntry->large_scenery.tiles);
int16_t maxHeight = GetMaxSurfaceHeight(sceneryEntry->large_scenery.tiles);
if (_loc.z != 0)
{
maxHeight = _loc.z;
}
res->Position.z = maxHeight;
if (!map_check_free_elements_and_reorganise(totalNumTiles))
{
log_error("No free map elements available");
return std::make_unique<LargeSceneryPlaceActionResult>(GameActions::Status::NoFreeElements);
}
uint8_t tileNum = 0;
for (rct_large_scenery_tile* tile = sceneryEntry->large_scenery.tiles; tile->x_offset != -1; tile++, tileNum++)
{
auto curTile = CoordsXY{ tile->x_offset, tile->y_offset }.Rotate(_loc.direction);
curTile.x += _loc.x;
curTile.y += _loc.y;
int32_t zLow = tile->z_offset + maxHeight;
int32_t zHigh = tile->z_clearance + zLow;
QuarterTile quarterTile = QuarterTile{ static_cast<uint8_t>(tile->flags >> 12), 0 }.Rotate(_loc.direction);
if (!map_can_construct_with_clear_at(
{ curTile, zLow, zHigh }, &map_place_scenery_clear_func, quarterTile, GetFlags(), &supportsCost,
CREATE_CROSSING_MODE_NONE))
{
return std::make_unique<LargeSceneryPlaceActionResult>(
GameActions::Status::NoClearance, gGameCommandErrorText, gCommonFormatArgs);
}
res->GroundFlags = gMapGroundFlags & (ELEMENT_IS_ABOVE_GROUND | ELEMENT_IS_UNDERGROUND);
if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST))
{
footpath_remove_litter({ curTile, zLow });
if (!gCheatsDisableClearanceChecks)
{
wall_remove_at({ curTile, zLow, zHigh });
}
}
TileElement* newTileElement = tile_element_insert(
CoordsXYZ{ curTile.x, curTile.y, zLow }, quarterTile.GetBaseQuarterOccupied());
Guard::Assert(newTileElement != nullptr);
map_animation_create(MAP_ANIMATION_TYPE_LARGE_SCENERY, { curTile, zLow });
newTileElement->SetType(TILE_ELEMENT_TYPE_LARGE_SCENERY);
newTileElement->SetClearanceZ(zHigh);
auto newSceneryElement = newTileElement->AsLargeScenery();
SetNewLargeSceneryElement(*newSceneryElement, tileNum);
if (tileNum == 0)
{
res->tileElement = newTileElement;
}
map_invalidate_tile_full(curTile);
}
// Allocate banner after all tiles to ensure banner id doesn't need to be freed.
if (sceneryEntry->large_scenery.scrolling_mode != SCROLLING_MODE_NONE)
{
if (_bannerId == BANNER_INDEX_NULL)
{
log_error("No free banners available");
return MakeResult(GameActions::Status::NoFreeElements, STR_TOO_MANY_BANNERS_IN_GAME);
}
auto banner = GetBanner(_bannerId);
if (!banner->IsNull())
{
log_error("No free banners available");
return std::make_unique<LargeSceneryPlaceActionResult>(GameActions::Status::NoFreeElements);
}
banner->text = {};
banner->colour = 2;
banner->text_colour = 2;
banner->flags = BANNER_FLAG_IS_LARGE_SCENERY;
banner->type = 0;
banner->position = TileCoordsXY(_loc);
ride_id_t rideIndex = banner_get_closest_ride_index({ _loc, maxHeight });
if (rideIndex != RIDE_ID_NULL)
{
banner->ride_index = rideIndex;
banner->flags |= BANNER_FLAG_LINKED_TO_RIDE;
}
}
// Force ride construction to recheck area
_currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_RECHECK;
res->Cost = (sceneryEntry->large_scenery.price * 10) + supportsCost;
return res;
}
int16_t LargeSceneryPlaceAction::GetTotalNumTiles(rct_large_scenery_tile* tiles) const
{
uint32_t totalNumTiles = 0;
for (rct_large_scenery_tile* tile = tiles; tile->x_offset != -1; tile++)
{
totalNumTiles++;
}
return totalNumTiles;
}
int16_t LargeSceneryPlaceAction::GetMaxSurfaceHeight(rct_large_scenery_tile* tiles) const
{
int16_t maxHeight = -1;
for (rct_large_scenery_tile* tile = tiles; tile->x_offset != -1; tile++)
{
auto curTile = CoordsXY{ tile->x_offset, tile->y_offset }.Rotate(_loc.direction);
curTile.x += _loc.x;
curTile.y += _loc.y;
if (!map_is_location_valid(curTile))
{
continue;
}
auto* surfaceElement = map_get_surface_element_at(curTile);
if (surfaceElement == nullptr)
continue;
int32_t baseZ = surfaceElement->GetBaseZ();
int32_t slope = surfaceElement->GetSlope();
if ((slope & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) != TILE_ELEMENT_SLOPE_FLAT)
{
baseZ += LAND_HEIGHT_STEP;
if (slope & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT)
{
baseZ += LAND_HEIGHT_STEP;
}
}
if (baseZ > maxHeight)
{
maxHeight = baseZ;
}
}
return maxHeight;
}
void LargeSceneryPlaceAction::SetNewLargeSceneryElement(LargeSceneryElement& sceneryElement, uint8_t tileNum) const
{
sceneryElement.SetDirection(_loc.direction);
sceneryElement.SetEntryIndex(_sceneryType);
sceneryElement.SetSequenceIndex(tileNum);
sceneryElement.SetPrimaryColour(_primaryColour);
sceneryElement.SetSecondaryColour(_secondaryColour);
if (_bannerId != BANNER_INDEX_NULL)
{
sceneryElement.SetBannerIndex(_bannerId);
}
if (GetFlags() & GAME_COMMAND_FLAG_GHOST)
{
sceneryElement.SetGhost(true);
}
}

Some files were not shown because too many files have changed in this diff Show More