Merge pull request #8914 from duncanspumpkin/buy_land_rights

Add landset/buyrights action
This commit is contained in:
Duncan 2019-05-12 19:38:39 +01:00 committed by GitHub
commit 62e188586c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 501 additions and 326 deletions

View File

@ -119,6 +119,7 @@
9346F9DC208A191900C77D91 /* GuestPathfinding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9346F9D7208A191900C77D91 /* GuestPathfinding.cpp */; };
9346F9DD208A191900C77D91 /* GuestPathfinding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9346F9D7208A191900C77D91 /* GuestPathfinding.cpp */; };
937A92152242CDAA00B09278 /* LandSmoothAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 937A92142242CDAA00B09278 /* LandSmoothAction.hpp */; };
937A92132242CCB300B09278 /* LandBuyRightsAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 937A92122242CCB300B09278 /* LandBuyRightsAction.hpp */; };
939A359A20C12FC800630B3F /* Paint.Litter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 939A359720C12FC700630B3F /* Paint.Litter.cpp */; };
939A359B20C12FC800630B3F /* Paint.Misc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 939A359820C12FC700630B3F /* Paint.Misc.cpp */; };
939A359C20C12FC800630B3F /* Paint.Sprite.h in Headers */ = {isa = PBXBuildFile; fileRef = 939A359920C12FC700630B3F /* Paint.Sprite.h */; };
@ -1243,6 +1244,7 @@
9350B52820B46E0900897BC5 /* ftrender.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ftrender.h; sourceTree = "<group>"; };
9350B52920B46E0900897BC5 /* ft2build.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ft2build.h; sourceTree = "<group>"; };
937A92142242CDAA00B09278 /* LandSmoothAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LandSmoothAction.hpp; sourceTree = "<group>"; };
937A92122242CCB300B09278 /* LandBuyRightsAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LandBuyRightsAction.hpp; sourceTree = "<group>"; };
939A359720C12FC700630B3F /* Paint.Litter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Paint.Litter.cpp; sourceTree = "<group>"; };
939A359820C12FC700630B3F /* Paint.Misc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Paint.Misc.cpp; sourceTree = "<group>"; };
939A359920C12FC700630B3F /* Paint.Sprite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Paint.Sprite.h; sourceTree = "<group>"; };
@ -2155,6 +2157,7 @@
C9C630BD2235A9C6009AD16E /* FootpathPlaceFromTrackAction.hpp */,
C9C630BB2235A7F9009AD16E /* WaterLowerAction.hpp */,
C9C630BC2235A7F9009AD16E /* WaterRaiseAction.hpp */,
937A92122242CCB300B09278 /* LandBuyRightsAction.hpp */,
C9C630BA2235A7E7009AD16E /* LandLowerAction.hpp */,
C9C630B92235A7E7009AD16E /* LandRaiseAction.hpp */,
937A92142242CDAA00B09278 /* LandSmoothAction.hpp */,
@ -3535,6 +3538,7 @@
2A43D2C02225B91A00E8F73B /* RideSetVehiclesAction.hpp in Headers */,
2ADE2F1C2244187B002598AF /* ParkSetNameAction.hpp in Headers */,
2AA050322209A8E300D3A922 /* StaffSetCostumeAction.hpp in Headers */,
937A92132242CCB300B09278 /* LandBuyRightsAction.hpp in Headers */,
2A61CAF72229E5720095AD67 /* FootpathPlaceAction.hpp in Headers */,
2A43D2C22225B91A00E8F73B /* LoadOrQuitAction.hpp in Headers */,
C62D838B1FD36D6F008C04F1 /* EditorObjectSelectionSession.h in Headers */,

View File

@ -15,6 +15,7 @@
#include <openrct2/Context.h>
#include <openrct2/Game.h>
#include <openrct2/Input.h>
#include <openrct2/actions/LandBuyRightsAction.hpp>
#include <openrct2/drawing/Drawing.h>
#include <openrct2/localisation/Localisation.h>
#include <openrct2/world/Park.h>
@ -364,11 +365,13 @@ static void window_land_rights_tool_update_land_rights(int16_t x, int16_t y)
if (!state_changed)
return;
_landRightsCost = game_do_command(
gMapSelectPositionA.x, GAME_COMMAND_FLAG_NO_SPEND, gMapSelectPositionA.y,
(_landRightsMode == LAND_RIGHTS_MODE_BUY_LAND) ? BUY_LAND_RIGHTS_FLAG_BUY_LAND
: BUY_LAND_RIGHTS_FLAG_BUY_CONSTRUCTION_RIGHTS,
GAME_COMMAND_BUY_LAND_RIGHTS, gMapSelectPositionB.x, gMapSelectPositionB.y);
auto landBuyRightsAction = LandBuyRightsAction(
{ gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y },
(_landRightsMode == LAND_RIGHTS_MODE_BUY_LAND) ? LandBuyRightSetting::BuyLand
: LandBuyRightSetting::BuyConstructionRights);
auto res = GameActions::Query(&landBuyRightsAction);
_landRightsCost = res->Error == GA_ERROR::OK ? res->Cost : MONEY32_UNDEFINED;
}
/**
@ -407,21 +410,20 @@ static void window_land_rights_tooldown(rct_window* w, rct_widgetindex widgetInd
{
if (x != LOCATION_NULL)
{
gGameCommandErrorTitle = STR_CANT_BUY_LAND;
game_do_command(
gMapSelectPositionA.x, GAME_COMMAND_FLAG_APPLY, gMapSelectPositionA.y, BUY_LAND_RIGHTS_FLAG_BUY_LAND,
GAME_COMMAND_BUY_LAND_RIGHTS, gMapSelectPositionB.x, gMapSelectPositionB.y);
auto landBuyRightsAction = LandBuyRightsAction(
{ gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y },
LandBuyRightSetting::BuyLand);
GameActions::Execute(&landBuyRightsAction);
}
}
else
{
if (x != LOCATION_NULL)
{
gGameCommandErrorTitle = STR_CANT_BUY_CONSTRUCTION_RIGHTS_HERE;
game_do_command(
gMapSelectPositionA.x, GAME_COMMAND_FLAG_APPLY, gMapSelectPositionA.y,
BUY_LAND_RIGHTS_FLAG_BUY_CONSTRUCTION_RIGHTS, GAME_COMMAND_BUY_LAND_RIGHTS, gMapSelectPositionB.x,
gMapSelectPositionB.y);
auto landBuyRightsAction = LandBuyRightsAction(
{ gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y },
LandBuyRightSetting::BuyConstructionRights);
GameActions::Execute(&landBuyRightsAction);
}
}
}
@ -436,21 +438,20 @@ static void window_land_rights_tooldrag(rct_window* w, rct_widgetindex widgetInd
{
if (x != LOCATION_NULL)
{
gGameCommandErrorTitle = STR_CANT_BUY_LAND;
game_do_command(
gMapSelectPositionA.x, GAME_COMMAND_FLAG_APPLY, gMapSelectPositionA.y, BUY_LAND_RIGHTS_FLAG_BUY_LAND,
GAME_COMMAND_BUY_LAND_RIGHTS, gMapSelectPositionB.x, gMapSelectPositionB.y);
auto landBuyRightsAction = LandBuyRightsAction(
{ gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y },
LandBuyRightSetting::BuyLand);
GameActions::Execute(&landBuyRightsAction);
}
}
else
{
if (x != LOCATION_NULL)
{
gGameCommandErrorTitle = STR_CANT_BUY_CONSTRUCTION_RIGHTS_HERE;
game_do_command(
gMapSelectPositionA.x, GAME_COMMAND_FLAG_APPLY, gMapSelectPositionA.y,
BUY_LAND_RIGHTS_FLAG_BUY_CONSTRUCTION_RIGHTS, GAME_COMMAND_BUY_LAND_RIGHTS, gMapSelectPositionB.x,
gMapSelectPositionB.y);
auto landBuyRightsAction = LandBuyRightsAction(
{ gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y },
LandBuyRightSetting::BuyConstructionRights);
GameActions::Execute(&landBuyRightsAction);
}
}
}

View File

@ -18,6 +18,7 @@
#include <openrct2/Game.h>
#include <openrct2/Input.h>
#include <openrct2/OpenRCT2.h>
#include <openrct2/actions/LandSetRightsAction.hpp>
#include <openrct2/actions/SurfaceSetStyleAction.hpp>
#include <openrct2/audio/audio.h>
#include <openrct2/localisation/Localisation.h>
@ -505,10 +506,10 @@ static void window_map_tooldrag(rct_window* w, rct_widgetindex widgetIndex, int3
case WIDX_SET_LAND_RIGHTS:
if (gMapSelectFlags & MAP_SELECT_FLAG_ENABLE)
{
gGameCommandErrorTitle = 0;
game_do_command(
gMapSelectPositionA.x, GAME_COMMAND_FLAG_APPLY, gMapSelectPositionA.y, _activeTool,
GAME_COMMAND_SET_LAND_OWNERSHIP, gMapSelectPositionB.x, gMapSelectPositionB.y);
auto landSetRightsAction = LandSetRightsAction(
{ gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y },
LandSetRightSetting::SetOwnershipWithChecks, _activeTool << 4);
GameActions::Execute(&landSetRightsAction);
}
break;
}
@ -614,10 +615,10 @@ static void window_map_scrollmousedown(rct_window* w, int32_t scrollIndex, int32
gMapSelectPositionB.y = mapY + size;
map_invalidate_selection_rect();
gGameCommandErrorTitle = 0;
game_do_command(
gMapSelectPositionA.x, GAME_COMMAND_FLAG_APPLY, gMapSelectPositionA.y, _activeTool, GAME_COMMAND_SET_LAND_OWNERSHIP,
gMapSelectPositionB.x, gMapSelectPositionB.y);
auto landSetRightsAction = LandSetRightsAction(
{ gMapSelectPositionA.x, gMapSelectPositionA.y, gMapSelectPositionB.x, gMapSelectPositionB.y },
LandSetRightSetting::SetOwnershipWithChecks, _activeTool << 4);
GameActions::Execute(&landSetRightsAction);
}
}

View File

@ -16,6 +16,8 @@
#include "GameState.h"
#include "OpenRCT2.h"
#include "ParkImporter.h"
#include "actions/LandBuyRightsAction.hpp"
#include "actions/LandSetRightsAction.hpp"
#include "audio/audio.h"
#include "interface/Viewport.h"
#include "localisation/Localisation.h"
@ -193,7 +195,14 @@ namespace Editor
{
int32_t mapSize = gMapSize;
game_do_command(64, 1, 64, 2, GAME_COMMAND_SET_LAND_OWNERSHIP, (mapSize - 3) * 32, (mapSize - 3) * 32);
MapRange range = { 64, 64, (mapSize - 3) * 32, (mapSize - 3) * 32 };
auto landSetRightsAction = LandSetRightsAction(range, LandSetRightSetting::SetForSale);
landSetRightsAction.SetFlags(GAME_COMMAND_FLAG_NO_SPEND);
GameActions::Execute(&landSetRightsAction);
auto landBuyRightsAction = LandBuyRightsAction(range, LandBuyRightSetting::BuyLand);
landBuyRightsAction.SetFlags(GAME_COMMAND_FLAG_NO_SPEND);
GameActions::Execute(&landBuyRightsAction);
}
/**

View File

@ -1191,7 +1191,7 @@ GAME_COMMAND_POINTER* new_game_command_table[GAME_COMMAND_COUNT] = {
nullptr,
nullptr,
nullptr,
game_command_buy_land_rights,
nullptr,
game_command_place_park_entrance,
nullptr,
game_command_set_maze_track,
@ -1212,7 +1212,7 @@ GAME_COMMAND_POINTER* new_game_command_table[GAME_COMMAND_COUNT] = {
nullptr,
nullptr,
nullptr,
game_command_set_land_ownership,
nullptr,
nullptr,
nullptr,
nullptr,

View File

@ -54,18 +54,18 @@ enum GAME_COMMAND
GAME_COMMAND_SET_STAFF_ORDERS, // GA
GAME_COMMAND_SET_PARK_NAME, // GA
GAME_COMMAND_SET_PARK_OPEN, // GA
GAME_COMMAND_BUY_LAND_RIGHTS,
GAME_COMMAND_PLACE_PARK_ENTRANCE, // GA
GAME_COMMAND_REMOVE_PARK_ENTRANCE, // GA
GAME_COMMAND_SET_MAZE_TRACK, // GA
GAME_COMMAND_SET_PARK_ENTRANCE_FEE, // GA
GAME_COMMAND_SET_STAFF_COLOUR, // GA
GAME_COMMAND_PLACE_WALL, // GA
GAME_COMMAND_REMOVE_WALL, // GA
GAME_COMMAND_PLACE_LARGE_SCENERY, // GA
GAME_COMMAND_REMOVE_LARGE_SCENERY, // GA
GAME_COMMAND_SET_CURRENT_LOAN, // GA
GAME_COMMAND_SET_RESEARCH_FUNDING, // GA
GAME_COMMAND_BUY_LAND_RIGHTS, // GA
GAME_COMMAND_PLACE_PARK_ENTRANCE, // GA
GAME_COMMAND_REMOVE_PARK_ENTRANCE, // GA
GAME_COMMAND_SET_MAZE_TRACK, // GA
GAME_COMMAND_SET_PARK_ENTRANCE_FEE, // GA
GAME_COMMAND_SET_STAFF_COLOUR, // GA
GAME_COMMAND_PLACE_WALL, // GA
GAME_COMMAND_REMOVE_WALL, // GA
GAME_COMMAND_PLACE_LARGE_SCENERY, // GA
GAME_COMMAND_REMOVE_LARGE_SCENERY, // GA
GAME_COMMAND_SET_CURRENT_LOAN, // GA
GAME_COMMAND_SET_RESEARCH_FUNDING, // GA
GAME_COMMAND_PLACE_TRACK_DESIGN,
GAME_COMMAND_START_MARKETING_CAMPAIGN, // GA
GAME_COMMAND_PLACE_MAZE_DESIGN,
@ -75,13 +75,13 @@ enum GAME_COMMAND
GAME_COMMAND_SET_WALL_COLOUR, // GA
GAME_COMMAND_SET_LARGE_SCENERY_COLOUR, // GA
GAME_COMMAND_SET_BANNER_COLOUR, // GA
GAME_COMMAND_SET_LAND_OWNERSHIP,
GAME_COMMAND_CLEAR_SCENERY, // GA
GAME_COMMAND_SET_BANNER_NAME, // GA
GAME_COMMAND_SET_SIGN_NAME, // GA
GAME_COMMAND_SET_BANNER_STYLE, // GA
GAME_COMMAND_SET_SIGN_STYLE, // GA
GAME_COMMAND_SET_PLAYER_GROUP, // GA
GAME_COMMAND_SET_LAND_OWNERSHIP, // GA
GAME_COMMAND_CLEAR_SCENERY, // GA
GAME_COMMAND_SET_BANNER_NAME, // GA
GAME_COMMAND_SET_SIGN_NAME, // GA
GAME_COMMAND_SET_BANNER_STYLE, // GA
GAME_COMMAND_SET_SIGN_STYLE, // GA
GAME_COMMAND_SET_PLAYER_GROUP, // GA
GAME_COMMAND_MODIFY_GROUPS,
GAME_COMMAND_KICK_PLAYER,
GAME_COMMAND_CHEAT,

View File

@ -22,9 +22,11 @@
#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"
@ -135,9 +137,11 @@ namespace GameActions
Register<LargeSceneryPlaceAction>();
Register<LargeSceneryRemoveAction>();
Register<LargeScenerySetColourAction>();
Register<LandBuyRightsAction>();
Register<LandLowerAction>();
Register<LandRaiseAction>();
Register<LandSetHeightAction>();
Register<LandSetRightsAction>();
Register<LandSmoothAction>();
Register<TrackPlaceAction>();
Register<TrackRemoveAction>();

View File

@ -0,0 +1,183 @@
/*****************************************************************************
* Copyright (c) 2014-2019 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"
enum class LandBuyRightSetting : uint8_t
{
BuyLand,
BuyConstructionRights,
Count
};
DEFINE_GAME_ACTION(LandBuyRightsAction, GAME_COMMAND_BUY_LAND_RIGHTS, GameActionResult)
{
private:
MapRange _range;
uint8_t _setting = static_cast<uint8_t>(LandBuyRightSetting::Count);
constexpr static rct_string_id _ErrorTitles[] = { STR_CANT_BUY_LAND, STR_CANT_BUY_CONSTRUCTION_RIGHTS_HERE };
public:
LandBuyRightsAction() = default;
LandBuyRightsAction(MapRange range, LandBuyRightSetting setting)
: _range(range)
, _setting(static_cast<uint8_t>(setting))
{
}
LandBuyRightsAction(CoordsXY coord, LandBuyRightSetting setting)
: _range(coord.x, coord.y, coord.x, coord.y)
, _setting(static_cast<uint8_t>(setting))
{
}
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags();
}
void Serialise(DataSerialiser & stream) override
{
GameAction::Serialise(stream);
stream << DS_TAG(_range) << DS_TAG(_setting);
}
GameActionResult::Ptr Query() const override
{
return QueryExecute(false);
}
GameActionResult::Ptr Execute() const override
{
return QueryExecute(true);
}
private:
GameActionResult::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.x, centre.y);
res->Position = centre;
res->ExpenditureType = RCT_EXPENDITURE_TYPE_LAND_PURCHASE;
// Game command modified to accept selection size
for (auto y = validRange.GetTop(); y <= validRange.GetBottom(); y += 32)
{
for (auto x = validRange.GetLeft(); x <= validRange.GetRight(); x += 32)
{
auto result = map_buy_land_rights_for_tile({ x, y }, isExecuting);
if (result->Error == GA_ERROR::OK)
{
res->Cost += result->Cost;
}
}
}
if (isExecuting)
{
map_count_remaining_land_rights();
}
return res;
}
GameActionResult::Ptr map_buy_land_rights_for_tile(const CoordsXY loc, bool isExecuting) const
{
if (_setting >= static_cast<uint8_t>(LandBuyRightSetting::Count))
{
log_warning("Tried calling buy land rights with an incorrect setting. setting = %u", _setting);
return MakeResult(GA_ERROR::INVALID_PARAMETERS, _ErrorTitles[0], STR_NONE);
}
SurfaceElement* surfaceElement = map_get_surface_element_at(loc)->AsSurface();
if (surfaceElement == nullptr)
{
log_error("Could not find surface. x = %d, y = %d", loc.x, loc.y);
return MakeResult(GA_ERROR::INVALID_PARAMETERS, _ErrorTitles[_setting], STR_NONE);
}
auto res = MakeResult();
switch (static_cast<LandBuyRightSetting>(_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(GA_ERROR::NOT_OWNED, _ErrorTitles[_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(GA_ERROR::NOT_OWNED, _ErrorTitles[_setting], STR_CONSTRUCTION_RIGHTS_NOT_FOR_SALE);
}
if (isExecuting)
{
surfaceElement->SetOwnership(surfaceElement->GetOwnership() | OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED);
uint16_t baseHeight = surfaceElement->base_height * 8;
map_invalidate_tile(loc.x, loc.y, baseHeight, baseHeight + 16);
}
res->Cost = gConstructionRightsPrice;
return res;
default:
log_warning("Tried calling buy land rights with an incorrect setting. setting = %u", _setting);
return MakeResult(GA_ERROR::INVALID_PARAMETERS, _ErrorTitles[0], STR_NONE);
}
}
};

View File

@ -0,0 +1,233 @@
/*****************************************************************************
* Copyright (c) 2014-2019 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"
enum class LandSetRightSetting : uint8_t
{
UnownLand,
UnownConstructionRights,
SetForSale,
SetConstructionRightsForSale,
SetOwnershipWithChecks,
Count
};
DEFINE_GAME_ACTION(LandSetRightsAction, GAME_COMMAND_SET_LAND_OWNERSHIP, GameActionResult)
{
private:
MapRange _range;
uint8_t _setting = static_cast<uint8_t>(LandSetRightSetting::Count);
uint8_t _ownership = 0;
public:
LandSetRightsAction() = default;
LandSetRightsAction(MapRange range, LandSetRightSetting setting, uint8_t ownership = 0)
: _range(range)
, _setting(static_cast<uint8_t>(setting))
, _ownership(ownership)
{
}
LandSetRightsAction(CoordsXY coord, LandSetRightSetting setting, uint8_t ownership = 0)
: _range(coord.x, coord.y, coord.x, coord.y)
, _setting(static_cast<uint8_t>(setting))
, _ownership(ownership)
{
}
uint16_t GetActionFlags() const override
{
return GameAction::GetActionFlags() | GA_FLAGS::EDITOR_ONLY;
}
void Serialise(DataSerialiser & stream) override
{
GameAction::Serialise(stream);
stream << DS_TAG(_range) << DS_TAG(_setting) << DS_TAG(_ownership);
}
GameActionResult::Ptr Query() const override
{
return QueryExecute(false);
}
GameActionResult::Ptr Execute() const override
{
return QueryExecute(true);
}
private:
GameActionResult::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.x, centre.y);
res->Position = centre;
res->ExpenditureType = RCT_EXPENDITURE_TYPE_LAND_PURCHASE;
if (!(gScreenFlags & SCREEN_FLAGS_EDITOR) && !gCheatsSandboxMode)
{
return MakeResult(GA_ERROR::NOT_IN_EDITOR_MODE, STR_NONE, STR_LAND_NOT_FOR_SALE);
}
// Game command modified to accept selection size
for (auto y = validRange.GetTop(); y <= validRange.GetBottom(); y += 32)
{
for (auto x = validRange.GetLeft(); x <= validRange.GetRight(); x += 32)
{
auto result = map_buy_land_rights_for_tile({ x, y }, isExecuting);
if (result->Error == GA_ERROR::OK)
{
res->Cost += result->Cost;
}
}
}
if (isExecuting)
{
map_count_remaining_land_rights();
audio_play_sound_at_location(SOUND_PLACE_ITEM, centre.x, centre.y, centre.z);
}
return res;
}
GameActionResult::Ptr map_buy_land_rights_for_tile(const CoordsXY loc, bool isExecuting) const
{
SurfaceElement* surfaceElement = map_get_surface_element_at(loc)->AsSurface();
if (surfaceElement == nullptr)
{
log_error("Could not find surface. x = %d, y = %d", loc.x, loc.y);
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE, STR_NONE);
}
auto res = MakeResult();
switch (static_cast<LandSetRightSetting>(_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 baseHeight = surfaceElement->base_height * 8;
map_invalidate_tile(loc.x, loc.y, baseHeight, baseHeight + 16);
}
return res;
case LandSetRightSetting::SetForSale:
if (isExecuting)
{
surfaceElement->SetOwnership(surfaceElement->GetOwnership() | OWNERSHIP_AVAILABLE);
uint16_t baseHeight = surfaceElement->base_height * 8;
map_invalidate_tile(loc.x, loc.y, baseHeight, baseHeight + 16);
}
return res;
case LandSetRightSetting::SetConstructionRightsForSale:
if (isExecuting)
{
surfaceElement->SetOwnership(surfaceElement->GetOwnership() | OWNERSHIP_CONSTRUCTION_RIGHTS_AVAILABLE);
uint16_t baseHeight = surfaceElement->base_height * 8;
map_invalidate_tile(loc.x, loc.y, baseHeight, baseHeight + 16);
}
return res;
case LandSetRightSetting::SetOwnershipWithChecks:
{
if (_ownership == surfaceElement->GetOwnership())
{
return res;
}
TileElement* tileElement = map_get_first_element_at(loc.x / 32, loc.y / 32);
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(GA_ERROR::INVALID_PARAMETERS, STR_NONE, STR_NONE);
}
}
};

View File

@ -32,7 +32,7 @@
// This string specifies which version of network stream current build uses.
// It is used for making sure only compatible builds get connected, even within
// single OpenRCT2 version.
#define NETWORK_STREAM_VERSION "27"
#define NETWORK_STREAM_VERSION "28"
#define NETWORK_STREAM_ID OPENRCT2_VERSION "-" NETWORK_STREAM_VERSION
static Peep* _pickup_peep = nullptr;

View File

@ -13,6 +13,7 @@
#include "../OpenRCT2.h"
#include "../actions/FootpathPlaceAction.hpp"
#include "../actions/FootpathRemoveAction.hpp"
#include "../actions/LandSetRightsAction.hpp"
#include "../core/Guard.hpp"
#include "../localisation/Localisation.h"
#include "../management/Finance.h"
@ -1238,7 +1239,9 @@ static void footpath_fix_ownership(int32_t x, int32_t y)
ownership = OWNERSHIP_UNOWNED;
}
map_buy_land_rights(x, y, x, y, BUY_LAND_RIGHTS_FLAG_SET_OWNERSHIP_WITH_CHECKS, (ownership << 4) | GAME_COMMAND_FLAG_APPLY);
auto landSetRightsAction = LandSetRightsAction({ x, y }, LandSetRightSetting::SetOwnershipWithChecks, ownership);
landSetRightsAction.SetFlags(GAME_COMMAND_FLAG_NO_SPEND);
GameActions::Execute(&landSetRightsAction);
}
static bool get_next_direction(int32_t edges, int32_t* direction)

View File

@ -19,6 +19,7 @@
#include "../actions/LandLowerAction.hpp"
#include "../actions/LandRaiseAction.hpp"
#include "../actions/LandSetHeightAction.hpp"
#include "../actions/LandSetRightsAction.hpp"
#include "../actions/LargeSceneryRemoveAction.hpp"
#include "../actions/ParkEntranceRemoveAction.hpp"
#include "../actions/SmallSceneryRemoveAction.hpp"
@ -924,52 +925,6 @@ int32_t tile_element_get_corner_height(const TileElement* tileElement, int32_t d
return map_get_corner_height(z, slope, direction);
}
static money32 map_set_land_ownership(uint8_t flags, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint8_t newOwnership)
{
gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LAND_PURCHASE;
if (!(flags & GAME_COMMAND_FLAG_APPLY))
return 0;
// Clamp to maximum addressable element to prevent long loop spamming the log
x1 = std::clamp<int16_t>(x1, 32, gMapSizeUnits - 32);
y1 = std::clamp<int16_t>(y1, 32, gMapSizeUnits - 32);
x2 = std::clamp<int16_t>(x2, 32, gMapSizeUnits - 32);
y2 = std::clamp<int16_t>(y2, 32, gMapSizeUnits - 32);
gMapLandRightsUpdateSuccess = false;
map_buy_land_rights(x1, y1, x2, y2, BUY_LAND_RIGHTS_FLAG_SET_OWNERSHIP_WITH_CHECKS, flags | (newOwnership << 8));
if (!gMapLandRightsUpdateSuccess)
return 0;
int16_t x = std::clamp<int16_t>(x1, 32, gMapSizeUnits - 32);
int16_t y = std::clamp<int16_t>(y1, 32, gMapSizeUnits - 32);
x += 16;
y += 16;
int16_t z = tile_element_height(x, y);
audio_play_sound_at_location(SOUND_PLACE_ITEM, x, y, z);
return 0;
}
/**
*
* rct2: 0x006648E3
*/
void game_command_set_land_ownership(
int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, int32_t* edi, int32_t* ebp)
{
int32_t flags = *ebx & 0xFF;
*ebx = map_set_land_ownership(flags, *eax & 0xFFFF, *ecx & 0xFFFF, *edi & 0xFFFF, *ebp & 0xFFFF, *edx & 0xFF);
if (flags & GAME_COMMAND_FLAG_APPLY)
{
map_count_remaining_land_rights();
}
}
uint8_t map_get_lowest_land_height(int32_t xMin, int32_t xMax, int32_t yMin, int32_t yMax)
{
xMin = std::max(xMin, 32);
@ -1648,7 +1603,13 @@ void map_remove_out_of_range_elements()
{
if (x == 0 || y == 0 || x >= mapMaxXY || y >= mapMaxXY)
{
map_buy_land_rights(x, y, x, y, BUY_LAND_RIGHTS_FLAG_UNOWN_TILE, GAME_COMMAND_FLAG_APPLY);
// Note this purposely does not use LandSetRightsAction as X Y coordinates are outside of normal range.
auto surfaceElement = map_get_surface_element_at({ x, y });
if (surfaceElement != nullptr)
{
surfaceElement->AsSurface()->SetOwnership(OWNERSHIP_UNOWNED);
update_park_fences_around_tile({ x, y });
}
clear_elements_at(x, y);
}
}

View File

@ -188,8 +188,6 @@ void rotate_map_coordinates(int16_t* x, int16_t* y, int32_t rotation);
LocationXY16 coordinate_3d_to_2d(const LocationXYZ16* coordinate_3d, int32_t rotation);
money32 map_clear_scenery(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t clear, int32_t flags);
void game_command_set_land_ownership(
int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp);
void game_command_place_park_entrance(
int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp);
void game_command_set_banner_name(

View File

@ -203,213 +203,6 @@ void park_set_name(const char* name)
}
}
static money32 map_buy_land_rights_for_tile(int32_t x, int32_t y, int32_t setting, int32_t flags)
{
SurfaceElement* surfaceElement = map_get_surface_element_at({ x, y })->AsSurface();
if (surfaceElement == nullptr)
return MONEY32_UNDEFINED;
switch (setting)
{
case BUY_LAND_RIGHTS_FLAG_BUY_LAND: // 0
if ((surfaceElement->GetOwnership() & OWNERSHIP_OWNED) != 0)
{ // If the land is already owned
return 0;
}
if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) != 0
|| (surfaceElement->GetOwnership() & OWNERSHIP_AVAILABLE) == 0)
{
gGameCommandErrorText = STR_LAND_NOT_FOR_SALE;
return MONEY32_UNDEFINED;
}
if (flags & GAME_COMMAND_FLAG_APPLY)
{
surfaceElement->SetOwnership(OWNERSHIP_OWNED);
update_park_fences_around_tile({ x, y });
}
return gLandPrice;
case BUY_LAND_RIGHTS_FLAG_UNOWN_TILE: // 1
if (flags & GAME_COMMAND_FLAG_APPLY)
{
surfaceElement->SetOwnership(
surfaceElement->GetOwnership() & ~(OWNERSHIP_OWNED | OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED));
update_park_fences_around_tile({ x, y });
}
return 0;
case BUY_LAND_RIGHTS_FLAG_BUY_CONSTRUCTION_RIGHTS: // 2
if ((surfaceElement->GetOwnership() & (OWNERSHIP_OWNED | OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED)) != 0)
{ // If the land or construction rights are already owned
return 0;
}
if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) != 0
|| (surfaceElement->GetOwnership() & OWNERSHIP_CONSTRUCTION_RIGHTS_AVAILABLE) == 0)
{
gGameCommandErrorText = STR_CONSTRUCTION_RIGHTS_NOT_FOR_SALE;
return MONEY32_UNDEFINED;
}
if (flags & GAME_COMMAND_FLAG_APPLY)
{
surfaceElement->SetOwnership(surfaceElement->GetOwnership() | OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED);
uint16_t baseHeight = surfaceElement->base_height * 8;
map_invalidate_tile(x, y, baseHeight, baseHeight + 16);
}
return gConstructionRightsPrice;
case BUY_LAND_RIGHTS_FLAG_UNOWN_CONSTRUCTION_RIGHTS: // 3
if (flags & GAME_COMMAND_FLAG_APPLY)
{
surfaceElement->SetOwnership(surfaceElement->GetOwnership() & ~OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED);
uint16_t baseHeight = surfaceElement->base_height * 8;
map_invalidate_tile(x, y, baseHeight, baseHeight + 16);
}
return 0;
case BUY_LAND_RIGHTS_FLAG_SET_FOR_SALE: // 4
if (flags & GAME_COMMAND_FLAG_APPLY)
{
surfaceElement->SetOwnership(surfaceElement->GetOwnership() | OWNERSHIP_AVAILABLE);
uint16_t baseHeight = surfaceElement->base_height * 8;
map_invalidate_tile(x, y, baseHeight, baseHeight + 16);
}
return 0;
case BUY_LAND_RIGHTS_FLAG_SET_CONSTRUCTION_RIGHTS_FOR_SALE: // 5
if (flags & GAME_COMMAND_FLAG_APPLY)
{
surfaceElement->SetOwnership(surfaceElement->GetOwnership() | OWNERSHIP_CONSTRUCTION_RIGHTS_AVAILABLE);
uint16_t baseHeight = surfaceElement->base_height * 8;
map_invalidate_tile(x, y, baseHeight, baseHeight + 16);
}
return 0;
case BUY_LAND_RIGHTS_FLAG_SET_OWNERSHIP_WITH_CHECKS:
{
if (!(gScreenFlags & SCREEN_FLAGS_EDITOR) && !gCheatsSandboxMode)
{
return MONEY32_UNDEFINED;
}
if (x <= 0 || y <= 0)
{
gGameCommandErrorText = STR_TOO_CLOSE_TO_EDGE_OF_MAP;
return MONEY32_UNDEFINED;
}
if (x >= gMapSizeUnits || y >= gMapSizeUnits)
{
gGameCommandErrorText = STR_TOO_CLOSE_TO_EDGE_OF_MAP;
return MONEY32_UNDEFINED;
}
uint8_t newOwnership = (flags & 0xFF00) >> 4;
if (newOwnership == surfaceElement->GetOwnership())
{
return 0;
}
TileElement* tileElement = map_get_first_element_at(x / 32, y / 32);
do
{
if (tileElement->GetType() == TILE_ELEMENT_TYPE_ENTRANCE
&& tileElement->AsEntrance()->GetEntranceType() == ENTRANCE_TYPE_PARK_ENTRANCE)
{
// Do not allow ownership of park entrance.
if (newOwnership == OWNERSHIP_OWNED || newOwnership == OWNERSHIP_AVAILABLE)
return 0;
// Allow construction rights available / for sale on park entrances on surface.
// There is no need to check the height if newOwnership is 0 (unowned and no rights available).
if ((newOwnership == OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED
|| newOwnership == OWNERSHIP_CONSTRUCTION_RIGHTS_AVAILABLE)
&& (tileElement->base_height - 3 > surfaceElement->base_height
|| tileElement->base_height < surfaceElement->base_height))
return 0;
}
} while (!(tileElement++)->IsLastForTile());
if (!(flags & GAME_COMMAND_FLAG_APPLY))
{
return gLandPrice;
}
if ((newOwnership & 0xF0) != 0)
{
gPeepSpawns.erase(
std::remove_if(
gPeepSpawns.begin(), gPeepSpawns.end(),
[x, y](const auto& spawn) { return floor2(spawn.x, 32) == x && floor2(spawn.y, 32) == y; }),
gPeepSpawns.end());
}
surfaceElement->SetOwnership(newOwnership);
update_park_fences_around_tile({ x, y });
gMapLandRightsUpdateSuccess = true;
return 0;
}
default:
log_warning("Tried calling map_buy_land_rights_for_tile() with an incorrect setting!");
assert(false);
return MONEY32_UNDEFINED;
}
}
int32_t map_buy_land_rights(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t setting, int32_t flags)
{
int32_t x, y, z;
money32 totalCost, cost;
gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LAND_PURCHASE;
if (x1 == 0 && y1 == 0)
{
x1 = x0;
y1 = y0;
}
x = (x0 + x1) / 2 + 16;
y = (y0 + y1) / 2 + 16;
z = tile_element_height(x, y);
gCommandPosition.x = x;
gCommandPosition.y = y;
gCommandPosition.z = z;
// Game command modified to accept selection size
totalCost = 0;
gGameCommandErrorText = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED;
if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) != 0 || game_is_not_paused() || gCheatsBuildInPauseMode)
{
for (y = y0; y <= y1; y += 32)
{
for (x = x0; x <= x1; x += 32)
{
cost = map_buy_land_rights_for_tile(x, y, setting, flags);
if (cost != MONEY32_UNDEFINED)
{
totalCost += cost;
}
}
}
}
return totalCost;
}
/**
*
* rct2: 0x006649BD
*/
void game_command_buy_land_rights(
int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, int32_t* edi, int32_t* ebp)
{
int32_t flags = *ebx & 0xFFFF;
*ebx = map_buy_land_rights((*eax & 0xFFFF), (*ecx & 0xFFFF), (*edi & 0xFFFF), (*ebp & 0xFFFF), (*edx & 0x00FF), flags);
// Too expensive to always call in map_buy_land_rights.
// It's already counted when the park is loaded, after
// that it should only be called for user actions.
if (flags & GAME_COMMAND_FLAG_APPLY)
{
map_count_remaining_land_rights();
}
}
void set_forced_park_rating(int32_t rating)
{
_forcedParkRating = rating;

View File

@ -87,17 +87,6 @@ namespace OpenRCT2
};
} // namespace OpenRCT2
enum
{
BUY_LAND_RIGHTS_FLAG_BUY_LAND,
BUY_LAND_RIGHTS_FLAG_UNOWN_TILE,
BUY_LAND_RIGHTS_FLAG_BUY_CONSTRUCTION_RIGHTS,
BUY_LAND_RIGHTS_FLAG_UNOWN_CONSTRUCTION_RIGHTS,
BUY_LAND_RIGHTS_FLAG_SET_FOR_SALE,
BUY_LAND_RIGHTS_FLAG_SET_CONSTRUCTION_RIGHTS_FOR_SALE,
BUY_LAND_RIGHTS_FLAG_SET_OWNERSHIP_WITH_CHECKS, // Used in scenario editor
};
extern rct_string_id gParkName;
extern uint32_t gParkNameArgs;
extern uint32_t gParkFlags;
@ -137,12 +126,8 @@ int32_t park_entrance_get_index(int32_t x, int32_t y, int32_t z);
void park_set_name(const char* name);
void park_set_entrance_fee(money32 value);
int32_t map_buy_land_rights(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t setting, int32_t flags);
void game_command_set_park_entrance_fee(
int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp);
void game_command_buy_land_rights(
int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp);
money16 park_get_entrance_fee();