mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2025-12-24 08:12:53 +01:00
Merge pull request #8807 from duncanspumpkin/footpath_ga
Footpath GameAction
This commit is contained in:
@@ -34,6 +34,9 @@
|
||||
2A5C1368221E9F9000F8C245 /* TrackRemoveAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2A5C1367221E9F9000F8C245 /* TrackRemoveAction.hpp */; };
|
||||
2A61CAFB2229E5C50095AD67 /* RideEntranceExitPlaceAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2A61CAFA2229E5C50095AD67 /* RideEntranceExitPlaceAction.hpp */; };
|
||||
2A61CAF92229E59F0095AD67 /* WaterSetHeightAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2A61CAF82229E59F0095AD67 /* WaterSetHeightAction.hpp */; };
|
||||
2A61CAF52229E5720095AD67 /* FootpathSceneryPlaceAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2A61CAF22229E5720095AD67 /* FootpathSceneryPlaceAction.hpp */; };
|
||||
2A61CAF62229E5720095AD67 /* FootpathSceneryRemoveAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2A61CAF32229E5720095AD67 /* FootpathSceneryRemoveAction.hpp */; };
|
||||
2A61CAF72229E5720095AD67 /* FootpathPlaceAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2A61CAF42229E5720095AD67 /* FootpathPlaceAction.hpp */; };
|
||||
2AA050322209A8E300D3A922 /* StaffSetCostumeAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2AA050302209A8E300D3A922 /* StaffSetCostumeAction.hpp */; };
|
||||
2AA050332209A8E300D3A922 /* StaffSetOrdersAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2AA050312209A8E300D3A922 /* StaffSetOrdersAction.hpp */; };
|
||||
2AAFD7FA220DD2DC002461A4 /* TrackPlaceAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2AAFD7F9220DD2DC002461A4 /* TrackPlaceAction.hpp */; };
|
||||
@@ -642,6 +645,9 @@
|
||||
2A5C1367221E9F9000F8C245 /* TrackRemoveAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TrackRemoveAction.hpp; sourceTree = "<group>"; };
|
||||
2A61CAFA2229E5C50095AD67 /* RideEntranceExitPlaceAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RideEntranceExitPlaceAction.hpp; sourceTree = "<group>"; };
|
||||
2A61CAF82229E59F0095AD67 /* WaterSetHeightAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = WaterSetHeightAction.hpp; sourceTree = "<group>"; };
|
||||
2A61CAF22229E5720095AD67 /* FootpathSceneryPlaceAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FootpathSceneryPlaceAction.hpp; sourceTree = "<group>"; };
|
||||
2A61CAF32229E5720095AD67 /* FootpathSceneryRemoveAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FootpathSceneryRemoveAction.hpp; sourceTree = "<group>"; };
|
||||
2A61CAF42229E5720095AD67 /* FootpathPlaceAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FootpathPlaceAction.hpp; sourceTree = "<group>"; };
|
||||
2AA050302209A8E300D3A922 /* StaffSetCostumeAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaffSetCostumeAction.hpp; sourceTree = "<group>"; };
|
||||
2AA050312209A8E300D3A922 /* StaffSetOrdersAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaffSetOrdersAction.hpp; sourceTree = "<group>"; };
|
||||
2AAFD7F9220DD2DC002461A4 /* TrackPlaceAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TrackPlaceAction.hpp; sourceTree = "<group>"; };
|
||||
@@ -2030,6 +2036,9 @@
|
||||
children = (
|
||||
2A61CAFA2229E5C50095AD67 /* RideEntranceExitPlaceAction.hpp */,
|
||||
2A61CAF82229E59F0095AD67 /* WaterSetHeightAction.hpp */,
|
||||
2A61CAF42229E5720095AD67 /* FootpathPlaceAction.hpp */,
|
||||
2A61CAF22229E5720095AD67 /* FootpathSceneryPlaceAction.hpp */,
|
||||
2A61CAF32229E5720095AD67 /* FootpathSceneryRemoveAction.hpp */,
|
||||
2ACBAB162226850A0034FB91 /* RideSetSetting.hpp */,
|
||||
2A43D2B92225B8D900E8F73B /* LoadOrQuitAction.hpp */,
|
||||
2A43D2B72225B8D900E8F73B /* RideSetVehiclesAction.hpp */,
|
||||
@@ -3346,6 +3355,7 @@
|
||||
C6352B951F477032006CCEE3 /* RideCreateAction.hpp in Headers */,
|
||||
C6352B851F477022006CCEE3 /* DataSerialiserTraits.h in Headers */,
|
||||
939A359F20C12FDE00630B3F /* Paint.Surface.h in Headers */,
|
||||
2A61CAF62229E5720095AD67 /* FootpathSceneryRemoveAction.hpp in Headers */,
|
||||
C67B28192002D7F200109C93 /* Window_internal.h in Headers */,
|
||||
C6352B971F477032006CCEE3 /* SetParkEntranceFeeAction.hpp in Headers */,
|
||||
2AA050332209A8E300D3A922 /* StaffSetOrdersAction.hpp in Headers */,
|
||||
@@ -3362,6 +3372,7 @@
|
||||
2A43D2BA2225B8D900E8F73B /* RideSetVehiclesAction.hpp in Headers */,
|
||||
2A43D2C02225B91A00E8F73B /* RideSetVehiclesAction.hpp in Headers */,
|
||||
2AA050322209A8E300D3A922 /* StaffSetCostumeAction.hpp in Headers */,
|
||||
2A61CAF72229E5720095AD67 /* FootpathPlaceAction.hpp in Headers */,
|
||||
2A43D2C22225B91A00E8F73B /* LoadOrQuitAction.hpp in Headers */,
|
||||
C62D838B1FD36D6F008C04F1 /* EditorObjectSelectionSession.h in Headers */,
|
||||
2A43D2BC2225B8D900E8F73B /* LoadOrQuitAction.hpp in Headers */,
|
||||
@@ -3369,6 +3380,7 @@
|
||||
939A35A220C12FFD00630B3F /* InteractiveConsole.h in Headers */,
|
||||
2ACBAB172226850A0034FB91 /* RideSetSetting.hpp in Headers */,
|
||||
93CBA4C320A7502E00867D56 /* Imaging.h in Headers */,
|
||||
2A61CAF52229E5720095AD67 /* FootpathSceneryPlaceAction.hpp in Headers */,
|
||||
9308DA05209908090079EE96 /* Surface.h in Headers */,
|
||||
93DE9753209C3C1000FB1CC8 /* GameState.h in Headers */,
|
||||
C6352B841F477022006CCEE3 /* DataSerialiser.h in Headers */,
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <openrct2/Game.h>
|
||||
#include <openrct2/Input.h>
|
||||
#include <openrct2/OpenRCT2.h>
|
||||
#include <openrct2/actions/FootpathSceneryRemoveAction.hpp>
|
||||
#include <openrct2/actions/LargeSceneryRemoveAction.hpp>
|
||||
#include <openrct2/actions/SmallSceneryRemoveAction.hpp>
|
||||
#include <openrct2/actions/WallRemoveAction.hpp>
|
||||
@@ -507,16 +508,8 @@ static void viewport_interaction_remove_footpath(TileElement* tileElement, int32
|
||||
*/
|
||||
static void viewport_interaction_remove_footpath_item(TileElement* tileElement, int32_t x, int32_t y)
|
||||
{
|
||||
int32_t type = tileElement->AsPath()->GetPathEntryIndex();
|
||||
if (tileElement->AsPath()->IsQueue())
|
||||
type |= 0x80;
|
||||
|
||||
int32_t slopeData = tileElement->AsPath()->GetSlopeDirection();
|
||||
if (tileElement->AsPath()->IsSloped())
|
||||
slopeData |= FOOTPATH_PROPERTIES_FLAG_IS_SLOPED;
|
||||
|
||||
gGameCommandErrorTitle = STR_CANT_REMOVE_THIS;
|
||||
game_do_command(x, (slopeData << 8) | 1, y, (type << 8) | tileElement->base_height, GAME_COMMAND_PLACE_PATH, 0, 0);
|
||||
auto footpathSceneryRemoveAction = FootpathSceneryRemoveAction({ x, y, tileElement->base_height * 8 });
|
||||
GameActions::Execute(&footpathSceneryRemoveAction);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <openrct2/Game.h>
|
||||
#include <openrct2/Input.h>
|
||||
#include <openrct2/OpenRCT2.h>
|
||||
#include <openrct2/actions/FootpathPlaceAction.hpp>
|
||||
#include <openrct2/audio/audio.h>
|
||||
#include <openrct2/localisation/Localisation.h>
|
||||
#include <openrct2/object/ObjectLimits.h>
|
||||
@@ -847,7 +848,7 @@ static void window_footpath_set_selection_start_bridge_at_point(int32_t screenX,
|
||||
*/
|
||||
static void window_footpath_place_path_at_point(int32_t x, int32_t y)
|
||||
{
|
||||
int32_t interactionType, currentType, selectedType, z, cost;
|
||||
int32_t interactionType, currentType, selectedType, z;
|
||||
TileElement* tileElement;
|
||||
|
||||
if (_footpathErrorOccured)
|
||||
@@ -894,19 +895,22 @@ static void window_footpath_place_path_at_point(int32_t x, int32_t y)
|
||||
|
||||
// Try and place path
|
||||
gGameCommandErrorTitle = STR_CANT_BUILD_FOOTPATH_HERE;
|
||||
cost = footpath_place(selectedType, x, y, z, currentType, GAME_COMMAND_FLAG_APPLY);
|
||||
|
||||
if (cost == MONEY32_UNDEFINED)
|
||||
{
|
||||
_footpathErrorOccured = true;
|
||||
}
|
||||
else if (gFootpathPrice != 0)
|
||||
{
|
||||
// bp = RCT2_ADDRESS_COMMAND_MAP_Z
|
||||
// dx = RCT2_ADDRESS_COMMAND_MAP_Y
|
||||
// cx = RCT2_ADDRESS_COMMAND_MAP_X
|
||||
audio_play_sound_at_location(SOUND_PLACE_ITEM, gCommandPosition.x, gCommandPosition.y, gCommandPosition.z);
|
||||
}
|
||||
auto footpathPlaceAction = FootpathPlaceAction({ x, y, z * 8 }, currentType, selectedType);
|
||||
footpathPlaceAction.SetCallback([](const GameAction* ga, const GameActionResult* result) {
|
||||
if (result->Error == GA_ERROR::OK)
|
||||
{
|
||||
// Don't play sound if it is no cost to prevent multiple sounds. TODO: make this work in no money scenarios
|
||||
if (result->Cost != 0)
|
||||
{
|
||||
audio_play_sound_at_location(SOUND_PLACE_ITEM, result->Position.x, result->Position.y, result->Position.z);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_footpathErrorOccured = true;
|
||||
}
|
||||
});
|
||||
GameActions::Execute(&footpathPlaceAction);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -982,43 +986,44 @@ static void window_footpath_construct()
|
||||
footpath_get_next_path_info(&type, &x, &y, &z, &slope);
|
||||
|
||||
gGameCommandErrorTitle = STR_CANT_BUILD_FOOTPATH_HERE;
|
||||
money32 cost = footpath_place_remove_intersecting(
|
||||
type, x, y, z, slope, GAME_COMMAND_FLAG_APPLY, gFootpathConstructDirection);
|
||||
|
||||
if (cost != MONEY32_UNDEFINED)
|
||||
{
|
||||
audio_play_sound_at_location(
|
||||
SOUND_PLACE_ITEM, gFootpathConstructFromPosition.x, gFootpathConstructFromPosition.y,
|
||||
gFootpathConstructFromPosition.z);
|
||||
|
||||
if (gFootpathConstructSlope == 0)
|
||||
auto footpathPlaceAction = FootpathPlaceAction({ x, y, z * 8 }, slope, type, gFootpathConstructDirection);
|
||||
footpathPlaceAction.SetCallback([=](const GameAction* ga, const GameActionResult* result) {
|
||||
if (result->Error == GA_ERROR::OK)
|
||||
{
|
||||
gFootpathConstructValidDirections = 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
gFootpathConstructValidDirections = gFootpathConstructDirection;
|
||||
}
|
||||
audio_play_sound_at_location(SOUND_PLACE_ITEM, result->Position.x, result->Position.y, result->Position.z);
|
||||
|
||||
if (gFootpathGroundFlags & ELEMENT_IS_UNDERGROUND)
|
||||
{
|
||||
viewport_set_visibility(1);
|
||||
if (gFootpathConstructSlope == 0)
|
||||
{
|
||||
gFootpathConstructValidDirections = 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
gFootpathConstructValidDirections = gFootpathConstructDirection;
|
||||
}
|
||||
|
||||
if (gFootpathGroundFlags & ELEMENT_IS_UNDERGROUND)
|
||||
{
|
||||
viewport_set_visibility(1);
|
||||
}
|
||||
|
||||
// If we have just built an upwards slope, the next path to construct is
|
||||
// a bit higher. Note that the z returned by footpath_get_next_path_info
|
||||
// already is lowered if we are building a downwards slope.
|
||||
if (gFootpathConstructSlope == 2)
|
||||
{
|
||||
gFootpathConstructFromPosition.z = (z + 2) * 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
gFootpathConstructFromPosition.z = z * 8;
|
||||
}
|
||||
|
||||
gFootpathConstructFromPosition.x = x;
|
||||
gFootpathConstructFromPosition.y = y;
|
||||
}
|
||||
|
||||
// If we have just built an upwards slope, the next path to construct is
|
||||
// a bit higher. Note that the z returned by footpath_get_next_path_info
|
||||
// already is lowered if we are building a downwards slope.
|
||||
if (gFootpathConstructSlope == 2)
|
||||
{
|
||||
z += 2;
|
||||
}
|
||||
|
||||
gFootpathConstructFromPosition.x = x;
|
||||
gFootpathConstructFromPosition.y = y;
|
||||
gFootpathConstructFromPosition.z = z << 3;
|
||||
}
|
||||
|
||||
window_footpath_set_enabled_and_pressed_widgets();
|
||||
window_footpath_set_enabled_and_pressed_widgets();
|
||||
});
|
||||
GameActions::Execute(&footpathPlaceAction);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <openrct2/OpenRCT2.h>
|
||||
#include <openrct2/ParkImporter.h>
|
||||
#include <openrct2/actions/ClearAction.hpp>
|
||||
#include <openrct2/actions/FootpathSceneryPlaceAction.hpp>
|
||||
#include <openrct2/actions/LoadOrQuitAction.hpp>
|
||||
#include <openrct2/actions/PauseToggleAction.hpp>
|
||||
#include <openrct2/actions/SmallSceneryPlaceAction.hpp>
|
||||
@@ -1816,14 +1817,18 @@ static void window_top_toolbar_scenery_tool_down(int16_t x, int16_t y, rct_windo
|
||||
}
|
||||
case SCENERY_TYPE_PATH_ITEM:
|
||||
{
|
||||
int32_t flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_PATH_SCENERY | (parameter_1 & 0xFF00);
|
||||
auto pathItemType = parameter_3 & 0xFF;
|
||||
int32_t z = (parameter_2 & 0xFF) * 8;
|
||||
auto footpathSceneryPlaceAction = FootpathSceneryPlaceAction({ gridX, gridY, z }, pathItemType);
|
||||
|
||||
gGameCommandErrorTitle = STR_CANT_POSITION_THIS_HERE;
|
||||
int32_t cost = game_do_command(gridX, flags, gridY, parameter_2, GAME_COMMAND_PLACE_PATH, parameter_3, 0);
|
||||
if (cost != MONEY32_UNDEFINED)
|
||||
{
|
||||
audio_play_sound_at_location(SOUND_PLACE_ITEM, gCommandPosition.x, gCommandPosition.y, gCommandPosition.z);
|
||||
}
|
||||
footpathSceneryPlaceAction.SetCallback([](const GameAction* ga, const GameActionResult* result) {
|
||||
if (result->Error != GA_ERROR::OK)
|
||||
{
|
||||
return;
|
||||
}
|
||||
audio_play_sound_at_location(SOUND_PLACE_ITEM, result->Position.x, result->Position.y, result->Position.z);
|
||||
});
|
||||
auto res = GameActions::Execute(&footpathSceneryPlaceAction);
|
||||
break;
|
||||
}
|
||||
case SCENERY_TYPE_WALL:
|
||||
@@ -2489,27 +2494,32 @@ static money32 try_place_ghost_scenery(
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
// Path Bits
|
||||
// 6e265b
|
||||
cost = game_do_command(
|
||||
map_tile.x,
|
||||
(parameter_1 & 0xFF00)
|
||||
| (GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_5
|
||||
| GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_PATH_SCENERY),
|
||||
map_tile.y, parameter_2, GAME_COMMAND_PLACE_PATH, parameter_3, 0);
|
||||
auto pathItemType = parameter_3 & 0xFF;
|
||||
int32_t z = (parameter_2 & 0xFF) * 8;
|
||||
auto footpathSceneryPlaceAction = FootpathSceneryPlaceAction({ map_tile.x, map_tile.y, z }, pathItemType);
|
||||
footpathSceneryPlaceAction.SetFlags(GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED);
|
||||
footpathSceneryPlaceAction.SetCallback([=](const GameAction* ga, const GameActionResult* result) {
|
||||
if (result->Error != GA_ERROR::OK)
|
||||
{
|
||||
return;
|
||||
}
|
||||
gSceneryGhostPosition.x = map_tile.x;
|
||||
gSceneryGhostPosition.y = map_tile.y;
|
||||
gSceneryGhostPosition.z = (parameter_2 & 0xFF);
|
||||
gSceneryPlacePathSlope = ((parameter_1 >> 8) & 0xFF);
|
||||
gSceneryPlacePathType = ((parameter_2 >> 8) & 0xFF);
|
||||
gSceneryGhostPathObjectType = parameter_3;
|
||||
|
||||
if (cost == MONEY32_UNDEFINED)
|
||||
return cost;
|
||||
|
||||
gSceneryGhostPosition.x = map_tile.x;
|
||||
gSceneryGhostPosition.y = map_tile.y;
|
||||
gSceneryGhostPosition.z = (parameter_2 & 0xFF);
|
||||
gSceneryPlacePathSlope = ((parameter_1 >> 8) & 0xFF);
|
||||
gSceneryPlacePathType = ((parameter_2 >> 8) & 0xFF);
|
||||
gSceneryGhostPathObjectType = parameter_3;
|
||||
|
||||
gSceneryGhostType |= SCENERY_GHOST_FLAG_1;
|
||||
gSceneryGhostType |= SCENERY_GHOST_FLAG_1;
|
||||
});
|
||||
auto res = GameActions::Execute(&footpathSceneryPlaceAction);
|
||||
if (res->Error != GA_ERROR::OK)
|
||||
return MONEY32_UNDEFINED;
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
// Walls
|
||||
// 6e26b0
|
||||
|
||||
@@ -1277,7 +1277,7 @@ GAME_COMMAND_POINTER* new_game_command_table[GAME_COMMAND_COUNT] = {
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
game_command_place_footpath,
|
||||
nullptr,
|
||||
game_command_place_footpath_from_track,
|
||||
nullptr,
|
||||
game_command_change_surface_style,
|
||||
|
||||
@@ -35,9 +35,9 @@ enum GAME_COMMAND
|
||||
GAME_COMMAND_REMOVE_SCENERY, // GA
|
||||
GAME_COMMAND_PLACE_SCENERY, // GA
|
||||
GAME_COMMAND_SET_WATER_HEIGHT, // GA
|
||||
GAME_COMMAND_PLACE_PATH,
|
||||
GAME_COMMAND_PLACE_PATH, // GA
|
||||
GAME_COMMAND_PLACE_PATH_FROM_TRACK,
|
||||
GAME_COMMAND_REMOVE_PATH,
|
||||
GAME_COMMAND_REMOVE_PATH, // GA
|
||||
GAME_COMMAND_CHANGE_SURFACE_STYLE,
|
||||
GAME_COMMAND_SET_RIDE_PRICE, // GA
|
||||
GAME_COMMAND_SET_GUEST_NAME, // GA
|
||||
@@ -90,10 +90,12 @@ enum GAME_COMMAND
|
||||
GAME_COMMAND_BALLOON_PRESS,
|
||||
GAME_COMMAND_MODIFY_TILE,
|
||||
GAME_COMMAND_EDIT_SCENARIO_OPTIONS,
|
||||
GAME_COMMAND_PLACE_PEEP_SPAWN, // GA, TODO: refactor to separate array for just game actions
|
||||
GAME_COMMAND_SET_CLIMATE, // GA
|
||||
GAME_COMMAND_SET_COLOUR_SCHEME, // GA
|
||||
GAME_COMMAND_SET_STAFF_COSTUME, // GA
|
||||
GAME_COMMAND_PLACE_PEEP_SPAWN, // GA, TODO: refactor to separate array for just game actions
|
||||
GAME_COMMAND_SET_CLIMATE, // GA
|
||||
GAME_COMMAND_SET_COLOUR_SCHEME, // GA
|
||||
GAME_COMMAND_SET_STAFF_COSTUME, // GA
|
||||
GAME_COMMAND_PLACE_FOOTPATH_SCENERY, // GA
|
||||
GAME_COMMAND_REMOVE_FOOTPATH_SCENERY, // GA
|
||||
GAME_COMMAND_COUNT,
|
||||
};
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "OpenRCT2.h"
|
||||
#include "ParkImporter.h"
|
||||
#include "PlatformEnvironment.h"
|
||||
#include "actions/FootpathPlaceAction.hpp"
|
||||
#include "actions/GameAction.h"
|
||||
#include "actions/RideEntranceExitPlaceAction.hpp"
|
||||
#include "actions/RideSetSetting.hpp"
|
||||
@@ -515,6 +516,16 @@ namespace OpenRCT2
|
||||
result.action->SetFlags(command.ebx & 0xFF);
|
||||
break;
|
||||
}
|
||||
case GAME_COMMAND_PLACE_PATH:
|
||||
{
|
||||
CoordsXYZ loc = { (int32_t)(command.eax & 0xFFFF), (int32_t)(command.ecx & 0xFFFF),
|
||||
(int32_t)(command.edx & 0xFF) * 8 };
|
||||
uint8_t slope = (command.ebx >> 8) & 0xFF;
|
||||
uint8_t type = (command.edx >> 8) & 0xFF;
|
||||
result.action = std::make_unique<FootpathPlaceAction>(loc, slope, type);
|
||||
result.action->SetFlags(command.ebx & 0xFF);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw std::runtime_error("Deprecated game command requires replay translation.");
|
||||
}
|
||||
|
||||
455
src/openrct2/actions/FootpathPlaceAction.hpp
Normal file
455
src/openrct2/actions/FootpathPlaceAction.hpp
Normal file
@@ -0,0 +1,455 @@
|
||||
/*****************************************************************************
|
||||
* 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 "../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(FootpathPlaceAction, GAME_COMMAND_PLACE_PATH, GameActionResult)
|
||||
{
|
||||
private:
|
||||
CoordsXYZ _loc;
|
||||
uint8_t _slope;
|
||||
uint8_t _type;
|
||||
uint8_t _direction = 0xFF;
|
||||
|
||||
public:
|
||||
FootpathPlaceAction() = default;
|
||||
FootpathPlaceAction(CoordsXYZ loc, uint8_t slope, uint8_t type, uint8_t direction = 0xFF)
|
||||
: _loc(loc)
|
||||
, _slope(slope)
|
||||
, _type(type)
|
||||
, _direction(direction)
|
||||
{
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
GameActionResult::Ptr Query() const override
|
||||
{
|
||||
GameActionResult::Ptr res = std::make_unique<GameActionResult>();
|
||||
res->Cost = 0;
|
||||
res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING;
|
||||
res->Position = _loc;
|
||||
res->Position.x += 16;
|
||||
res->Position.y += 16;
|
||||
|
||||
gFootpathGroundFlags = 0;
|
||||
|
||||
if (map_is_edge({ _loc.x, _loc.y }))
|
||||
{
|
||||
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_BUILD_FOOTPATH_HERE, STR_OFF_EDGE_OF_MAP);
|
||||
}
|
||||
|
||||
if (!((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode)
|
||||
&& !map_is_location_owned(_loc.x, _loc.y, _loc.z))
|
||||
{
|
||||
return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_BUILD_FOOTPATH_HERE, STR_LAND_NOT_OWNED_BY_PARK);
|
||||
}
|
||||
|
||||
if (_slope & SLOPE_IS_IRREGULAR_FLAG)
|
||||
{
|
||||
return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_BUILD_FOOTPATH_HERE, STR_LAND_SLOPE_UNSUITABLE);
|
||||
}
|
||||
|
||||
if (_loc.z / 8 < 2)
|
||||
{
|
||||
return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_BUILD_FOOTPATH_HERE, STR_TOO_LOW);
|
||||
}
|
||||
|
||||
if (_loc.z / 8 > 248)
|
||||
{
|
||||
return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_BUILD_FOOTPATH_HERE, STR_TOO_HIGH);
|
||||
}
|
||||
|
||||
if (_direction != 0xFF && _direction > 15)
|
||||
{
|
||||
log_error("Direction invalid. direction = %u", _direction);
|
||||
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_BUILD_FOOTPATH_HERE);
|
||||
}
|
||||
|
||||
footpath_provisional_remove();
|
||||
auto tileElement = map_get_footpath_element_slope((_loc.x / 32), (_loc.y / 32), _loc.z / 8, _slope);
|
||||
if (tileElement == nullptr)
|
||||
{
|
||||
return ElementInsertQuery(std::move(res));
|
||||
}
|
||||
else
|
||||
{
|
||||
return ElementUpdateQuery(tileElement, std::move(res));
|
||||
}
|
||||
}
|
||||
|
||||
GameActionResult::Ptr Execute() const override
|
||||
{
|
||||
GameActionResult::Ptr res = std::make_unique<GameActionResult>();
|
||||
res->Cost = 0;
|
||||
res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING;
|
||||
res->Position = _loc;
|
||||
res->Position.x += 16;
|
||||
res->Position.y += 16;
|
||||
|
||||
if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST))
|
||||
{
|
||||
footpath_interrupt_peeps(_loc.x, _loc.y, _loc.z);
|
||||
}
|
||||
|
||||
gFootpathGroundFlags = 0;
|
||||
|
||||
// Force ride construction to recheck area
|
||||
_currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_RECHECK;
|
||||
|
||||
if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST))
|
||||
{
|
||||
if (_direction != 0xFF && !gCheatsDisableClearanceChecks)
|
||||
{
|
||||
// It is possible, let's remove walls between the old and new piece of path
|
||||
auto zLow = _loc.z / 8;
|
||||
auto zHigh = zLow + 4;
|
||||
wall_remove_intersecting_walls(
|
||||
_loc.x, _loc.y, zLow, zHigh + ((_slope & TILE_ELEMENT_SURFACE_RAISED_CORNERS_MASK) ? 2 : 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.x / 32), (_loc.y / 32), _loc.z / 8, _slope);
|
||||
if (tileElement == nullptr)
|
||||
{
|
||||
return ElementInsertExecute(std::move(res));
|
||||
}
|
||||
else
|
||||
{
|
||||
return ElementUpdateExecute(tileElement, std::move(res));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
GameActionResult::Ptr ElementUpdateQuery(PathElement * pathElement, GameActionResult::Ptr res) const
|
||||
{
|
||||
const int32_t newFootpathType = (_type & (FOOTPATH_PROPERTIES_TYPE_MASK >> 4));
|
||||
const bool newPathIsQueue = ((_type >> 7) == 1);
|
||||
if (pathElement->GetPathEntryIndex() != newFootpathType || pathElement->IsQueue() != newPathIsQueue)
|
||||
{
|
||||
res->Cost += MONEY(6, 00);
|
||||
}
|
||||
|
||||
if (GetFlags() & GAME_COMMAND_FLAG_GHOST && !pathElement->IsGhost())
|
||||
{
|
||||
return MakeResult(GA_ERROR::UNKNOWN, STR_CANT_BUILD_FOOTPATH_HERE);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
GameActionResult::Ptr ElementUpdateExecute(PathElement * pathElement, GameActionResult::Ptr res) const
|
||||
{
|
||||
const int32_t newFootpathType = (_type & (FOOTPATH_PROPERTIES_TYPE_MASK >> 4));
|
||||
const bool newPathIsQueue = ((_type >> 7) == 1);
|
||||
if (pathElement->GetPathEntryIndex() != 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.x, _loc.y, (TileElement*)pathElement);
|
||||
}
|
||||
|
||||
pathElement->SetPathEntryIndex(_type);
|
||||
if (_type & (1 << 7))
|
||||
{
|
||||
pathElement->SetIsQueue(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
pathElement->SetIsQueue(false);
|
||||
}
|
||||
pathElement->SetAddition(0);
|
||||
pathElement->SetIsBroken(false);
|
||||
|
||||
RemoveIntersectingWalls(pathElement);
|
||||
return res;
|
||||
}
|
||||
|
||||
GameActionResult::Ptr ElementInsertQuery(GameActionResult::Ptr res) const
|
||||
{
|
||||
bool entrancePath = false, entranceIsSamePath = false;
|
||||
|
||||
if (!map_check_free_elements_and_reorganise(1))
|
||||
{
|
||||
return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_CANT_BUILD_FOOTPATH_HERE);
|
||||
}
|
||||
|
||||
res->Cost = MONEY(12, 00);
|
||||
|
||||
QuarterTile quarterTile{ 0b1111, 0 };
|
||||
auto zLow = _loc.z / 8;
|
||||
auto zHigh = zLow + 4;
|
||||
if (_slope & FOOTPATH_PROPERTIES_FLAG_IS_SLOPED)
|
||||
{
|
||||
quarterTile = QuarterTile{ 0b1111, 0b1100 }.Rotate(_slope & TILE_ELEMENT_DIRECTION_MASK);
|
||||
zHigh += 2;
|
||||
}
|
||||
|
||||
auto entranceElement = map_get_park_entrance_element_at(_loc.x, _loc.y, zLow, 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.x, _loc.y, zLow, zHigh, &map_place_non_scenery_clear_func, quarterTile, GetFlags(), &res->Cost,
|
||||
crossingMode))
|
||||
{
|
||||
return MakeResult(GA_ERROR::NO_CLEARANCE, STR_CANT_BUILD_FOOTPATH_HERE, gGameCommandErrorText, gCommonFormatArgs);
|
||||
}
|
||||
|
||||
gFootpathGroundFlags = gMapGroundFlags;
|
||||
if (!gCheatsDisableClearanceChecks && (gMapGroundFlags & ELEMENT_IS_UNDERWATER))
|
||||
{
|
||||
return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_BUILD_FOOTPATH_HERE, STR_CANT_BUILD_THIS_UNDERWATER);
|
||||
}
|
||||
|
||||
auto tileElement = map_get_surface_element_at({ _loc.x, _loc.y });
|
||||
if (tileElement == nullptr)
|
||||
{
|
||||
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_BUILD_FOOTPATH_HERE);
|
||||
}
|
||||
auto surfaceElement = tileElement->AsSurface();
|
||||
int32_t supportHeight = zLow - surfaceElement->base_height;
|
||||
res->Cost += supportHeight < 0 ? MONEY(20, 00) : (supportHeight / 2) * MONEY(5, 00);
|
||||
|
||||
// Prevent the place sound from being spammed
|
||||
if (entranceIsSamePath)
|
||||
res->Cost = 0;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
GameActionResult::Ptr ElementInsertExecute(GameActionResult::Ptr res) const
|
||||
{
|
||||
bool entrancePath = false, entranceIsSamePath = false;
|
||||
|
||||
if (!(GetFlags() & (GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_GHOST)))
|
||||
{
|
||||
footpath_remove_litter(_loc.x, _loc.y, _loc.z);
|
||||
}
|
||||
|
||||
res->Cost = MONEY(12, 00);
|
||||
|
||||
QuarterTile quarterTile{ 0b1111, 0 };
|
||||
auto zLow = _loc.z / 8;
|
||||
auto zHigh = zLow + 4;
|
||||
if (_slope & FOOTPATH_PROPERTIES_FLAG_IS_SLOPED)
|
||||
{
|
||||
quarterTile = QuarterTile{ 0b1111, 0b1100 }.Rotate(_slope & TILE_ELEMENT_DIRECTION_MASK);
|
||||
zHigh += 2;
|
||||
}
|
||||
|
||||
auto entranceElement = map_get_park_entrance_element_at(_loc.x, _loc.y, zLow, 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.x, _loc.y, zLow, zHigh, &map_place_non_scenery_clear_func, quarterTile,
|
||||
GAME_COMMAND_FLAG_APPLY | GetFlags(), &res->Cost, crossingMode))
|
||||
{
|
||||
return MakeResult(GA_ERROR::NO_CLEARANCE, STR_CANT_BUILD_FOOTPATH_HERE, gGameCommandErrorText, gCommonFormatArgs);
|
||||
}
|
||||
|
||||
gFootpathGroundFlags = gMapGroundFlags;
|
||||
|
||||
auto tileElement = map_get_surface_element_at({ _loc.x, _loc.y });
|
||||
if (tileElement == nullptr)
|
||||
{
|
||||
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_BUILD_FOOTPATH_HERE);
|
||||
}
|
||||
auto surfaceElement = tileElement->AsSurface();
|
||||
int32_t supportHeight = zLow - surfaceElement->base_height;
|
||||
res->Cost += supportHeight < 0 ? MONEY(20, 00) : (supportHeight / 2) * 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.x, _loc.y);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tileElement = tile_element_insert(_loc.x / 32, _loc.y / 32, zLow, 0b1111);
|
||||
assert(tileElement != nullptr);
|
||||
tileElement->SetType(TILE_ELEMENT_TYPE_PATH);
|
||||
PathElement* pathElement = tileElement->AsPath();
|
||||
pathElement->clearance_height = zHigh;
|
||||
pathElement->SetPathEntryIndex(_type);
|
||||
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.x, _loc.y, 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.size() == 0)
|
||||
{
|
||||
gPeepSpawns.emplace_back();
|
||||
}
|
||||
PeepSpawn* peepSpawn = &gPeepSpawns[0];
|
||||
peepSpawn->x = _loc.x + (word_981D6C[direction].x * 15) + 16;
|
||||
peepSpawn->y = _loc.y + (word_981D6C[direction].y * 15) + 16;
|
||||
peepSpawn->direction = direction;
|
||||
peepSpawn->z = _loc.z;
|
||||
}
|
||||
|
||||
void RemoveIntersectingWalls(PathElement * pathElement) const
|
||||
{
|
||||
if (pathElement->IsSloped() && !(GetFlags() & GAME_COMMAND_FLAG_GHOST))
|
||||
{
|
||||
int32_t direction = pathElement->GetSlopeDirection();
|
||||
int32_t z = pathElement->base_height;
|
||||
wall_remove_intersecting_walls(_loc.x, _loc.y, z, z + 6, direction_reverse(direction));
|
||||
wall_remove_intersecting_walls(_loc.x, _loc.y, z, z + 6, direction);
|
||||
// Removing walls may have made the pointer invalid, so find it again
|
||||
auto tileElement = map_get_footpath_element(_loc.x / 32, _loc.y / 32, 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.x, _loc.y, (TileElement*)pathElement, GetFlags());
|
||||
|
||||
footpath_update_queue_chains();
|
||||
map_invalidate_tile_full(_loc.x, _loc.y);
|
||||
}
|
||||
|
||||
PathElement* map_get_footpath_element_slope(int32_t x, int32_t y, int32_t z, int32_t slope) const
|
||||
{
|
||||
TileElement* tileElement;
|
||||
bool isSloped = slope & FOOTPATH_PROPERTIES_FLAG_IS_SLOPED;
|
||||
|
||||
tileElement = map_get_first_element_at(x, y);
|
||||
do
|
||||
{
|
||||
if (tileElement->GetType() == TILE_ELEMENT_TYPE_PATH && tileElement->base_height == z
|
||||
&& (tileElement->AsPath()->IsSloped() == isSloped)
|
||||
&& (tileElement->AsPath()->GetSlopeDirection() == (slope & FOOTPATH_PROPERTIES_SLOPE_DIRECTION_MASK)))
|
||||
{
|
||||
return tileElement->AsPath();
|
||||
}
|
||||
} while (!(tileElement++)->IsLastForTile());
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
208
src/openrct2/actions/FootpathSceneryPlaceAction.hpp
Normal file
208
src/openrct2/actions/FootpathSceneryPlaceAction.hpp
Normal file
@@ -0,0 +1,208 @@
|
||||
/*****************************************************************************
|
||||
* 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 "../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(FootpathSceneryPlaceAction, GAME_COMMAND_PLACE_FOOTPATH_SCENERY, GameActionResult)
|
||||
{
|
||||
private:
|
||||
CoordsXYZ _loc;
|
||||
uint8_t _pathItemType;
|
||||
|
||||
public:
|
||||
FootpathSceneryPlaceAction() = default;
|
||||
FootpathSceneryPlaceAction(CoordsXYZ loc, uint8_t pathItemType)
|
||||
: _loc(loc)
|
||||
, _pathItemType(pathItemType)
|
||||
{
|
||||
}
|
||||
|
||||
uint16_t GetActionFlags() const override
|
||||
{
|
||||
return GameAction::GetActionFlags();
|
||||
}
|
||||
|
||||
void Serialise(DataSerialiser & stream) override
|
||||
{
|
||||
GameAction::Serialise(stream);
|
||||
|
||||
stream << DS_TAG(_loc) << DS_TAG(_pathItemType);
|
||||
}
|
||||
|
||||
GameActionResult::Ptr Query() const override
|
||||
{
|
||||
auto res = MakeResult();
|
||||
res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING;
|
||||
res->Position = _loc;
|
||||
if (!map_is_location_valid({ _loc.x, _loc.y }))
|
||||
{
|
||||
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_POSITION_THIS_HERE, STR_OFF_EDGE_OF_MAP);
|
||||
}
|
||||
|
||||
if (!((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode)
|
||||
&& !map_is_location_owned(_loc.x, _loc.y, _loc.z / 8))
|
||||
{
|
||||
return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_POSITION_THIS_HERE, STR_LAND_NOT_OWNED_BY_PARK);
|
||||
}
|
||||
|
||||
if (_loc.z / 8 < 2)
|
||||
{
|
||||
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_POSITION_THIS_HERE, STR_TOO_LOW);
|
||||
}
|
||||
|
||||
if (_loc.z / 8 > 248)
|
||||
{
|
||||
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_POSITION_THIS_HERE, STR_TOO_HIGH);
|
||||
}
|
||||
|
||||
auto tileElement = map_get_footpath_element(_loc.x / 32, _loc.y / 32, _loc.z / 8);
|
||||
auto pathElement = tileElement->AsPath();
|
||||
|
||||
if (pathElement == nullptr)
|
||||
{
|
||||
log_error("Could not find path element.");
|
||||
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_POSITION_THIS_HERE);
|
||||
}
|
||||
|
||||
// No change
|
||||
if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST) && pathElement->GetAddition() == _pathItemType
|
||||
&& !(pathElement->IsBroken()))
|
||||
{
|
||||
if (GetFlags() & GAME_COMMAND_FLAG_4)
|
||||
return MakeResult(GA_ERROR::UNKNOWN, STR_CANT_POSITION_THIS_HERE);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
if (_pathItemType != 0)
|
||||
{
|
||||
rct_scenery_entry* sceneryEntry = get_footpath_item_entry(_pathItemType - 1);
|
||||
if (sceneryEntry == nullptr)
|
||||
{
|
||||
return MakeResult(GA_ERROR::INVALID_PARAMETERS, 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(
|
||||
GA_ERROR::INVALID_PARAMETERS, 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(
|
||||
GA_ERROR::INVALID_PARAMETERS, 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(GA_ERROR::INVALID_PARAMETERS, STR_CANT_POSITION_THIS_HERE);
|
||||
}
|
||||
|
||||
if ((sceneryFlags & PATH_BIT_FLAG_IS_QUEUE_SCREEN) && !pathElement->IsQueue())
|
||||
{
|
||||
return MakeResult(
|
||||
GA_ERROR::INVALID_PARAMETERS, STR_CANT_POSITION_THIS_HERE, STR_CAN_ONLY_PLACE_THESE_ON_QUEUE_AREA);
|
||||
}
|
||||
|
||||
res->Cost = sceneryEntry->path_bit.price;
|
||||
}
|
||||
|
||||
if (GetFlags() & GAME_COMMAND_FLAG_4)
|
||||
return MakeResult(GA_ERROR::UNKNOWN, STR_CANT_POSITION_THIS_HERE);
|
||||
|
||||
// Should place a ghost?
|
||||
if (GetFlags() & GAME_COMMAND_FLAG_GHOST)
|
||||
{
|
||||
// Check if there is something on the path already
|
||||
if (pathElement->HasAddition())
|
||||
{
|
||||
return MakeResult(GA_ERROR::ITEM_ALREADY_PLACED, STR_CANT_POSITION_THIS_HERE);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
GameActionResult::Ptr Execute() const override
|
||||
{
|
||||
auto res = MakeResult();
|
||||
res->Position = _loc;
|
||||
res->ExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING;
|
||||
|
||||
auto tileElement = map_get_footpath_element(_loc.x / 32, _loc.y / 32, _loc.z / 8);
|
||||
auto pathElement = tileElement->AsPath();
|
||||
|
||||
if (pathElement == nullptr)
|
||||
{
|
||||
log_error("Could not find path element.");
|
||||
return MakeResult(GA_ERROR::INVALID_PARAMETERS, 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(GA_ERROR::INVALID_PARAMETERS, 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.x, _loc.y, _loc.z);
|
||||
}
|
||||
|
||||
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.x, _loc.y);
|
||||
return res;
|
||||
}
|
||||
};
|
||||
115
src/openrct2/actions/FootpathSceneryRemoveAction.hpp
Normal file
115
src/openrct2/actions/FootpathSceneryRemoveAction.hpp
Normal file
@@ -0,0 +1,115 @@
|
||||
/*****************************************************************************
|
||||
* 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 "../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(FootpathSceneryRemoveAction, GAME_COMMAND_REMOVE_FOOTPATH_SCENERY, GameActionResult)
|
||||
{
|
||||
private:
|
||||
CoordsXYZ _loc;
|
||||
|
||||
public:
|
||||
FootpathSceneryRemoveAction() = default;
|
||||
FootpathSceneryRemoveAction(CoordsXYZ loc)
|
||||
: _loc(loc)
|
||||
{
|
||||
}
|
||||
|
||||
uint16_t GetActionFlags() const override
|
||||
{
|
||||
return GameAction::GetActionFlags();
|
||||
}
|
||||
|
||||
void Serialise(DataSerialiser & stream) override
|
||||
{
|
||||
GameAction::Serialise(stream);
|
||||
|
||||
stream << DS_TAG(_loc);
|
||||
}
|
||||
|
||||
GameActionResult::Ptr Query() const override
|
||||
{
|
||||
if (!map_is_location_valid({ _loc.x, _loc.y }))
|
||||
{
|
||||
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REMOVE_THIS, STR_OFF_EDGE_OF_MAP);
|
||||
}
|
||||
|
||||
if (!((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode)
|
||||
&& !map_is_location_owned(_loc.x, _loc.y, _loc.z / 8))
|
||||
{
|
||||
return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_REMOVE_THIS, STR_LAND_NOT_OWNED_BY_PARK);
|
||||
}
|
||||
|
||||
if (_loc.z / 8 < 2)
|
||||
{
|
||||
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REMOVE_THIS, STR_TOO_LOW);
|
||||
}
|
||||
|
||||
if (_loc.z / 8 > 248)
|
||||
{
|
||||
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REMOVE_THIS, STR_TOO_HIGH);
|
||||
}
|
||||
|
||||
auto tileElement = map_get_footpath_element(_loc.x / 32, _loc.y / 32, _loc.z / 8);
|
||||
auto pathElement = tileElement->AsPath();
|
||||
|
||||
if (pathElement == nullptr)
|
||||
{
|
||||
log_error("Could not find path element.");
|
||||
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REMOVE_THIS);
|
||||
}
|
||||
|
||||
if (!pathElement->AdditionIsGhost() && (GetFlags() & GAME_COMMAND_FLAG_GHOST))
|
||||
{
|
||||
log_error("Tried to remove non ghost during ghost removal.");
|
||||
return MakeResult(GA_ERROR::DISALLOWED, STR_CANT_REMOVE_THIS);
|
||||
}
|
||||
auto res = MakeResult();
|
||||
res->Position = _loc;
|
||||
res->Cost = MONEY(0, 0);
|
||||
return res;
|
||||
}
|
||||
|
||||
GameActionResult::Ptr Execute() const override
|
||||
{
|
||||
auto tileElement = map_get_footpath_element(_loc.x / 32, _loc.y / 32, _loc.z / 8);
|
||||
auto pathElement = tileElement->AsPath();
|
||||
|
||||
if (!(GetFlags() & GAME_COMMAND_FLAG_GHOST))
|
||||
{
|
||||
footpath_interrupt_peeps(_loc.x, _loc.y, _loc.z);
|
||||
}
|
||||
|
||||
if (pathElement == nullptr)
|
||||
{
|
||||
log_error("Could not find path element.");
|
||||
return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_CANT_REMOVE_THIS);
|
||||
}
|
||||
|
||||
pathElement->SetAddition(0);
|
||||
map_invalidate_tile_full(_loc.x, _loc.y);
|
||||
|
||||
auto res = MakeResult();
|
||||
res->Position = _loc;
|
||||
res->Cost = MONEY(0, 0);
|
||||
return res;
|
||||
}
|
||||
};
|
||||
@@ -10,7 +10,10 @@
|
||||
#include "BannerSetNameAction.hpp"
|
||||
#include "ClearAction.hpp"
|
||||
#include "ClimateSetAction.hpp"
|
||||
#include "FootpathPlaceAction.hpp"
|
||||
#include "FootpathRemoveAction.hpp"
|
||||
#include "FootpathSceneryPlaceAction.hpp"
|
||||
#include "FootpathSceneryRemoveAction.hpp"
|
||||
#include "GameAction.h"
|
||||
#include "GuestSetNameAction.hpp"
|
||||
#include "LandSetHeightAction.hpp"
|
||||
@@ -55,7 +58,10 @@ namespace GameActions
|
||||
{
|
||||
Register<BannerSetNameAction>();
|
||||
Register<ClimateSetAction>();
|
||||
Register<FootpathPlaceAction>();
|
||||
Register<FootpathRemoveAction>();
|
||||
Register<FootpathSceneryPlaceAction>();
|
||||
Register<FootpathSceneryRemoveAction>();
|
||||
Register<GuestSetNameAction>();
|
||||
Register<MazeSetTrackAction>();
|
||||
Register<ParkMarketingAction>();
|
||||
|
||||
@@ -343,6 +343,31 @@ template<> struct DataSerializerTraits<CoordsXY>
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct DataSerializerTraits<CoordsXYZ>
|
||||
{
|
||||
static void encode(IStream* stream, const CoordsXYZ& coord)
|
||||
{
|
||||
stream->WriteValue(ByteSwapBE(coord.x));
|
||||
stream->WriteValue(ByteSwapBE(coord.y));
|
||||
stream->WriteValue(ByteSwapBE(coord.z));
|
||||
}
|
||||
|
||||
static void decode(IStream* stream, CoordsXYZ& coord)
|
||||
{
|
||||
auto x = ByteSwapBE(stream->ReadValue<int32_t>());
|
||||
auto y = ByteSwapBE(stream->ReadValue<int32_t>());
|
||||
auto z = ByteSwapBE(stream->ReadValue<int32_t>());
|
||||
coord = CoordsXYZ{ x, y, z };
|
||||
}
|
||||
|
||||
static void log(IStream* stream, const CoordsXYZ& coord)
|
||||
{
|
||||
char msg[128] = {};
|
||||
snprintf(msg, sizeof(msg), "CoordsXYZ(x = %d, y = %d, z = %d)", coord.x, coord.y, coord.z);
|
||||
stream->Write(msg, strlen(msg));
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct DataSerializerTraits<CoordsXYZD>
|
||||
{
|
||||
static void encode(IStream* stream, const CoordsXYZD& coord)
|
||||
|
||||
@@ -31,7 +31,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 "53"
|
||||
#define NETWORK_STREAM_VERSION "54"
|
||||
#define NETWORK_STREAM_ID OPENRCT2_VERSION "-" NETWORK_STREAM_VERSION
|
||||
|
||||
static Peep* _pickup_peep = nullptr;
|
||||
|
||||
@@ -153,6 +153,8 @@ const std::array<NetworkAction, NETWORK_PERMISSION_COUNT> NetworkActions::Action
|
||||
GAME_COMMAND_PLACE_PATH,
|
||||
GAME_COMMAND_PLACE_PATH_FROM_TRACK,
|
||||
GAME_COMMAND_REMOVE_PATH,
|
||||
GAME_COMMAND_PLACE_FOOTPATH_SCENERY,
|
||||
GAME_COMMAND_REMOVE_FOOTPATH_SCENERY,
|
||||
},
|
||||
},
|
||||
NetworkAction{
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "../Context.h"
|
||||
#include "../Game.h"
|
||||
#include "../OpenRCT2.h"
|
||||
#include "../actions/FootpathPlaceAction.hpp"
|
||||
#include "../actions/FootpathRemoveAction.hpp"
|
||||
#include "../core/Guard.hpp"
|
||||
#include "../localisation/Localisation.h"
|
||||
@@ -109,39 +110,6 @@ static bool entrance_has_direction(TileElement* tileElement, int32_t direction)
|
||||
return entrance_get_directions(tileElement) & (1 << (direction & 3));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x006A65AD
|
||||
*/
|
||||
static void automatically_set_peep_spawn(CoordsXYZ location)
|
||||
{
|
||||
uint8_t direction = 0;
|
||||
if (location.x != 32)
|
||||
{
|
||||
direction++;
|
||||
if (location.y != gMapSizeUnits - 32)
|
||||
{
|
||||
direction++;
|
||||
if (location.x != gMapSizeUnits - 32)
|
||||
{
|
||||
direction++;
|
||||
if (location.y != 32)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (gPeepSpawns.size() == 0)
|
||||
{
|
||||
gPeepSpawns.emplace_back();
|
||||
}
|
||||
PeepSpawn* peepSpawn = &gPeepSpawns[0];
|
||||
peepSpawn->x = location.x + (word_981D6C[direction].x * 15) + 16;
|
||||
peepSpawn->y = location.y + (word_981D6C[direction].y * 15) + 16;
|
||||
peepSpawn->direction = direction;
|
||||
peepSpawn->z = location.z;
|
||||
}
|
||||
|
||||
TileElement* map_get_footpath_element(int32_t x, int32_t y, int32_t z)
|
||||
{
|
||||
TileElement* tileElement;
|
||||
@@ -156,365 +124,11 @@ TileElement* map_get_footpath_element(int32_t x, int32_t y, int32_t z)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static TileElement* map_get_footpath_element_slope(int32_t x, int32_t y, int32_t z, int32_t slope)
|
||||
{
|
||||
TileElement* tileElement;
|
||||
bool isSloped = slope & FOOTPATH_PROPERTIES_FLAG_IS_SLOPED;
|
||||
|
||||
tileElement = map_get_first_element_at(x, y);
|
||||
do
|
||||
{
|
||||
if (tileElement->GetType() == TILE_ELEMENT_TYPE_PATH && tileElement->base_height == z
|
||||
&& (tileElement->AsPath()->IsSloped() == isSloped)
|
||||
&& (tileElement->AsPath()->GetSlopeDirection() == (slope & FOOTPATH_PROPERTIES_SLOPE_DIRECTION_MASK)))
|
||||
{
|
||||
return tileElement;
|
||||
}
|
||||
} while (!(tileElement++)->IsLastForTile());
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void loc_6A6620(int32_t flags, int32_t x, int32_t y, TileElement* tileElement)
|
||||
{
|
||||
if (tileElement->AsPath()->IsSloped() && !(flags & GAME_COMMAND_FLAG_GHOST))
|
||||
{
|
||||
int32_t direction = tileElement->AsPath()->GetSlopeDirection();
|
||||
int32_t z = tileElement->base_height;
|
||||
wall_remove_intersecting_walls(x, y, z, z + 6, direction_reverse(direction));
|
||||
wall_remove_intersecting_walls(x, y, z, z + 6, direction);
|
||||
// Removing walls may have made the pointer invalid, so find it again
|
||||
tileElement = map_get_footpath_element(x / 32, y / 32, z);
|
||||
}
|
||||
|
||||
if (!(flags & GAME_COMMAND_FLAG_PATH_SCENERY))
|
||||
footpath_connect_edges(x, y, tileElement, flags);
|
||||
|
||||
footpath_update_queue_chains();
|
||||
map_invalidate_tile_full(x, y);
|
||||
}
|
||||
|
||||
/** rct2: 0x0098D7EC */
|
||||
static constexpr const QuarterTile SlopedFootpathQuarterTiles[] = {
|
||||
{ 0b1111, 0b1100 }, { 0b1111, 0b1001 }, { 0b1111, 0b0011 }, { 0b1111, 0b0110 }
|
||||
};
|
||||
|
||||
static money32 footpath_element_insert(
|
||||
int32_t type, int32_t x, int32_t y, int32_t z, int32_t slope, int32_t flags, uint8_t pathItemType)
|
||||
{
|
||||
TileElement* tileElement;
|
||||
EntranceElement* entranceElement;
|
||||
int32_t zHigh;
|
||||
bool entrancePath = false, entranceIsSamePath = false;
|
||||
|
||||
if (!map_check_free_elements_and_reorganise(1))
|
||||
return MONEY32_UNDEFINED;
|
||||
|
||||
if ((flags & GAME_COMMAND_FLAG_APPLY) && !(flags & (GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_GHOST)))
|
||||
footpath_remove_litter(x, y, gCommandPosition.z);
|
||||
|
||||
// loc_6A649D:
|
||||
gFootpathPrice += MONEY(12, 00);
|
||||
|
||||
QuarterTile quarterTile{ 0b1111, 0 };
|
||||
zHigh = z + 4;
|
||||
if (slope & FOOTPATH_PROPERTIES_FLAG_IS_SLOPED)
|
||||
{
|
||||
quarterTile = SlopedFootpathQuarterTiles[slope & TILE_ELEMENT_DIRECTION_MASK];
|
||||
zHigh += 2;
|
||||
}
|
||||
|
||||
entranceElement = map_get_park_entrance_element_at(x, y, z, 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
|
||||
gFootpathPrice -= 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(
|
||||
x, y, z, zHigh, &map_place_non_scenery_clear_func, quarterTile, flags, &gFootpathPrice, crossingMode))
|
||||
return MONEY32_UNDEFINED;
|
||||
|
||||
gFootpathGroundFlags = gMapGroundFlags;
|
||||
if (!gCheatsDisableClearanceChecks && (gMapGroundFlags & ELEMENT_IS_UNDERWATER))
|
||||
{
|
||||
gGameCommandErrorText = STR_CANT_BUILD_THIS_UNDERWATER;
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
|
||||
tileElement = map_get_surface_element_at({ x, y });
|
||||
|
||||
if (tileElement == nullptr)
|
||||
{
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
|
||||
int32_t supportHeight = z - tileElement->base_height;
|
||||
gFootpathPrice += supportHeight < 0 ? MONEY(20, 00) : (supportHeight / 2) * MONEY(5, 00);
|
||||
|
||||
if (flags & GAME_COMMAND_FLAG_APPLY)
|
||||
{
|
||||
if (entrancePath)
|
||||
{
|
||||
if (!(flags & 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(x, y);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tileElement = tile_element_insert(x / 32, y / 32, z, 0x0F);
|
||||
assert(tileElement != nullptr);
|
||||
tileElement->SetType(TILE_ELEMENT_TYPE_PATH);
|
||||
PathElement* pathElement = tileElement->AsPath();
|
||||
pathElement->clearance_height = z + 4 + ((slope & FOOTPATH_PROPERTIES_SLOPE_DIRECTION_MASK) ? 2 : 0);
|
||||
pathElement->SetPathEntryIndex(type);
|
||||
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(pathItemType);
|
||||
pathElement->SetRideIndex(RIDE_ID_NULL);
|
||||
pathElement->SetAdditionStatus(255);
|
||||
pathElement->SetIsBroken(false);
|
||||
if (flags & GAME_COMMAND_FLAG_GHOST)
|
||||
pathElement->SetGhost(true);
|
||||
|
||||
footpath_queue_chain_reset();
|
||||
|
||||
if (!(flags & GAME_COMMAND_FLAG_PATH_SCENERY))
|
||||
footpath_remove_edges_at(x, y, tileElement);
|
||||
|
||||
if ((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !(flags & GAME_COMMAND_FLAG_GHOST))
|
||||
automatically_set_peep_spawn({ x, y, tileElement->base_height * 8 });
|
||||
|
||||
loc_6A6620(flags, x, y, tileElement);
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent the place sound from being spammed
|
||||
if (entranceIsSamePath)
|
||||
gFootpathPrice = 0;
|
||||
|
||||
return gParkFlags & PARK_FLAGS_NO_MONEY ? 0 : gFootpathPrice;
|
||||
}
|
||||
|
||||
static money32 footpath_element_update(
|
||||
int32_t x, int32_t y, TileElement* tileElement, int32_t type, int32_t flags, uint8_t pathItemType)
|
||||
{
|
||||
const int32_t newFootpathType = (type & (FOOTPATH_PROPERTIES_TYPE_MASK >> 4));
|
||||
const bool newPathIsQueue = ((type >> 7) == 1);
|
||||
|
||||
if (tileElement->AsPath()->GetPathEntryIndex() != newFootpathType || tileElement->AsPath()->IsQueue() != newPathIsQueue)
|
||||
{
|
||||
gFootpathPrice += MONEY(6, 00);
|
||||
}
|
||||
else if (pathItemType != 0)
|
||||
{
|
||||
if (!(flags & GAME_COMMAND_FLAG_GHOST) && tileElement->AsPath()->GetAddition() == pathItemType
|
||||
&& !(tileElement->AsPath()->IsBroken()))
|
||||
{
|
||||
if (flags & GAME_COMMAND_FLAG_4)
|
||||
return MONEY32_UNDEFINED;
|
||||
|
||||
return gParkFlags & PARK_FLAGS_NO_MONEY ? 0 : gFootpathPrice;
|
||||
}
|
||||
|
||||
if (pathItemType != 0)
|
||||
{
|
||||
rct_scenery_entry* scenery_entry = get_footpath_item_entry(pathItemType - 1);
|
||||
uint16_t unk6 = scenery_entry->path_bit.flags;
|
||||
|
||||
if ((unk6 & PATH_BIT_FLAG_DONT_ALLOW_ON_SLOPE) && tileElement->AsPath()->IsSloped())
|
||||
{
|
||||
gGameCommandErrorText = STR_CANT_BUILD_THIS_ON_SLOPED_FOOTPATH;
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
|
||||
if ((unk6 & PATH_BIT_FLAG_DONT_ALLOW_ON_QUEUE) && tileElement->AsPath()->IsQueue())
|
||||
{
|
||||
gGameCommandErrorText = STR_CANNOT_PLACE_THESE_ON_QUEUE_LINE_AREA;
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
|
||||
if (!(unk6 & (PATH_BIT_FLAG_JUMPING_FOUNTAIN_WATER | PATH_BIT_FLAG_JUMPING_FOUNTAIN_SNOW))
|
||||
&& (tileElement->AsPath()->GetEdges()) == 0x0F)
|
||||
{
|
||||
gGameCommandErrorText = STR_NONE;
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
|
||||
if ((unk6 & PATH_BIT_FLAG_IS_QUEUE_SCREEN) && !tileElement->AsPath()->IsQueue())
|
||||
{
|
||||
gGameCommandErrorText = STR_CAN_ONLY_PLACE_THESE_ON_QUEUE_AREA;
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
|
||||
gFootpathPrice += scenery_entry->path_bit.price;
|
||||
}
|
||||
|
||||
if (flags & GAME_COMMAND_FLAG_4)
|
||||
return MONEY32_UNDEFINED;
|
||||
|
||||
// Should place a ghost?
|
||||
if (flags & GAME_COMMAND_FLAG_GHOST)
|
||||
{
|
||||
// Check if there is something on the path already
|
||||
if (tileElement->AsPath()->HasAddition())
|
||||
{
|
||||
gGameCommandErrorText = STR_NONE;
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
|
||||
// There is nothing yet - check if we should place a ghost
|
||||
if (flags & GAME_COMMAND_FLAG_APPLY)
|
||||
tileElement->AsPath()->SetAdditionIsGhost(true);
|
||||
}
|
||||
|
||||
if (!(flags & GAME_COMMAND_FLAG_APPLY))
|
||||
return gParkFlags & PARK_FLAGS_NO_MONEY ? 0 : gFootpathPrice;
|
||||
|
||||
if ((pathItemType != 0 && !(flags & GAME_COMMAND_FLAG_GHOST))
|
||||
|| (pathItemType == 0 && tileElement->AsPath()->AdditionIsGhost()))
|
||||
{
|
||||
tileElement->AsPath()->SetAdditionIsGhost(false);
|
||||
}
|
||||
|
||||
tileElement->AsPath()->SetAddition(pathItemType);
|
||||
tileElement->AsPath()->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)
|
||||
{
|
||||
tileElement->AsPath()->SetAdditionStatus(255);
|
||||
}
|
||||
}
|
||||
map_invalidate_tile_full(x, y);
|
||||
return gParkFlags & PARK_FLAGS_NO_MONEY ? 0 : gFootpathPrice;
|
||||
}
|
||||
|
||||
if (flags & GAME_COMMAND_FLAG_4)
|
||||
return MONEY32_UNDEFINED;
|
||||
|
||||
if (flags & GAME_COMMAND_FLAG_APPLY)
|
||||
{
|
||||
footpath_queue_chain_reset();
|
||||
|
||||
if (!(flags & GAME_COMMAND_FLAG_PATH_SCENERY))
|
||||
footpath_remove_edges_at(x, y, tileElement);
|
||||
|
||||
tileElement->AsPath()->SetPathEntryIndex(type);
|
||||
if (type & (1 << 7))
|
||||
tileElement->AsPath()->SetIsQueue(true);
|
||||
else
|
||||
tileElement->AsPath()->SetIsQueue(false);
|
||||
tileElement->AsPath()->SetAddition(pathItemType);
|
||||
tileElement->AsPath()->SetIsBroken(false);
|
||||
|
||||
loc_6A6620(flags, x, y, tileElement);
|
||||
}
|
||||
|
||||
return gParkFlags & PARK_FLAGS_NO_MONEY ? 0 : gFootpathPrice;
|
||||
}
|
||||
|
||||
static money32 footpath_place_real(
|
||||
int32_t type, int32_t x, int32_t y, int32_t z, int32_t slope, int32_t flags, uint8_t pathItemType, bool clearDirection,
|
||||
int32_t direction)
|
||||
{
|
||||
TileElement* tileElement;
|
||||
|
||||
gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING;
|
||||
gCommandPosition.x = x + 16;
|
||||
gCommandPosition.y = y + 16;
|
||||
gCommandPosition.z = z * 8;
|
||||
|
||||
if (!(flags & GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED) && game_is_paused() && !gCheatsBuildInPauseMode)
|
||||
{
|
||||
gGameCommandErrorText = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED;
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
|
||||
if ((flags & GAME_COMMAND_FLAG_APPLY) && !(flags & GAME_COMMAND_FLAG_GHOST))
|
||||
footpath_interrupt_peeps(x, y, z * 8);
|
||||
|
||||
gFootpathPrice = 0;
|
||||
gFootpathGroundFlags = 0;
|
||||
|
||||
if (map_is_edge({ x, y }))
|
||||
{
|
||||
gGameCommandErrorText = STR_OFF_EDGE_OF_MAP;
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
|
||||
if (!((gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) || gCheatsSandboxMode) && !map_is_location_owned(x, y, z * 8))
|
||||
return MONEY32_UNDEFINED;
|
||||
|
||||
if (slope & SLOPE_IS_IRREGULAR_FLAG)
|
||||
{
|
||||
gGameCommandErrorText = STR_LAND_SLOPE_UNSUITABLE;
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
|
||||
if (z < 2)
|
||||
{
|
||||
gGameCommandErrorText = STR_TOO_LOW;
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
|
||||
if (z > 248)
|
||||
{
|
||||
gGameCommandErrorText = STR_TOO_HIGH;
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
|
||||
// Force ride construction to recheck area
|
||||
_currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_RECHECK;
|
||||
|
||||
if (gGameCommandNestLevel == 1 && !(flags & GAME_COMMAND_FLAG_GHOST))
|
||||
{
|
||||
LocationXYZ16 coord;
|
||||
coord.x = x + 16;
|
||||
coord.y = y + 16;
|
||||
coord.z = tile_element_height(coord.x, coord.y);
|
||||
network_set_player_last_action_coord(network_get_player_index(game_command_playerid), coord);
|
||||
|
||||
if (clearDirection && !gCheatsDisableClearanceChecks)
|
||||
{
|
||||
direction = direction & 0xF;
|
||||
// It is possible, let's remove walls between the old and new piece of path
|
||||
wall_remove_intersecting_walls(
|
||||
x, y, z, z + 4 + ((slope & TILE_ELEMENT_SURFACE_RAISED_CORNERS_MASK) ? 2 : 0), direction_reverse(direction));
|
||||
wall_remove_intersecting_walls(
|
||||
x - CoordsDirectionDelta[direction].x, y - CoordsDirectionDelta[direction].y, z, z + 4, direction);
|
||||
}
|
||||
}
|
||||
|
||||
footpath_provisional_remove();
|
||||
tileElement = map_get_footpath_element_slope((x / 32), (y / 32), z, slope);
|
||||
if (tileElement == nullptr)
|
||||
{
|
||||
return footpath_element_insert(type, x, y, z, slope, flags, pathItemType);
|
||||
}
|
||||
else
|
||||
{
|
||||
return footpath_element_update(x, y, tileElement, type, flags, pathItemType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x006BA23E
|
||||
@@ -534,18 +148,6 @@ void remove_banners_at_element(int32_t x, int32_t y, TileElement* tileElement)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x006A61DE
|
||||
*/
|
||||
void game_command_place_footpath(
|
||||
int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, [[maybe_unused]] int32_t* esi, int32_t* edi, int32_t* ebp)
|
||||
{
|
||||
*ebx = footpath_place_real(
|
||||
(*edx >> 8) & 0xFF, *eax & 0xFFFF, *ecx & 0xFFFF, *edx & 0xFF, (*ebx >> 8) & 0xFF, *ebx & 0xFF, *edi & 0xFF,
|
||||
(*ebp & FOOTPATH_CLEAR_DIRECTIONAL) >> 8, *ebp & 0xFF);
|
||||
}
|
||||
|
||||
static money32 footpath_place_from_track(
|
||||
int32_t type, int32_t x, int32_t y, int32_t z, int32_t slope, int32_t edges, int32_t flags)
|
||||
{
|
||||
@@ -711,18 +313,6 @@ void game_command_place_footpath_from_track(
|
||||
(*ebx >> 8) & 0xF, *ebx & 0xFF);
|
||||
}
|
||||
|
||||
money32 footpath_place(int32_t type, int32_t x, int32_t y, int32_t z, int32_t slope, int32_t flags)
|
||||
{
|
||||
return game_do_command(x, (slope << 8) | flags, y, (type << 8) | z, GAME_COMMAND_PLACE_PATH, 0, 0);
|
||||
}
|
||||
|
||||
money32 footpath_place_remove_intersecting(
|
||||
int32_t type, int32_t x, int32_t y, int32_t z, int32_t slope, int32_t flags, int32_t direction)
|
||||
{
|
||||
return game_do_command(
|
||||
x, (slope << 8) | flags, y, (type << 8) | z, GAME_COMMAND_PLACE_PATH, 0, FOOTPATH_CLEAR_DIRECTIONAL | direction);
|
||||
}
|
||||
|
||||
money32 footpath_remove(int32_t x, int32_t y, int32_t z, int32_t flags)
|
||||
{
|
||||
auto action = FootpathRemoveAction(x, y, z);
|
||||
@@ -747,11 +337,11 @@ money32 footpath_provisional_set(int32_t type, int32_t x, int32_t y, int32_t z,
|
||||
|
||||
footpath_provisional_remove();
|
||||
|
||||
cost = footpath_place(
|
||||
type, x, y, z, slope,
|
||||
GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_5 | GAME_COMMAND_FLAG_4 | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED
|
||||
| GAME_COMMAND_FLAG_APPLY);
|
||||
if (cost != MONEY32_UNDEFINED)
|
||||
auto footpathPlaceAction = FootpathPlaceAction({ x, y, z * 8 }, slope, type);
|
||||
footpathPlaceAction.SetFlags(GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED);
|
||||
auto res = GameActions::Execute(&footpathPlaceAction);
|
||||
cost = res->Error == GA_ERROR::OK ? res->Cost : MONEY32_UNDEFINED;
|
||||
if (res->Error == GA_ERROR::OK)
|
||||
{
|
||||
gFootpathProvisionalType = type;
|
||||
gFootpathProvisionalPosition.x = x;
|
||||
@@ -775,7 +365,7 @@ money32 footpath_provisional_set(int32_t type, int32_t x, int32_t y, int32_t z,
|
||||
|
||||
if (!scenery_tool_is_active())
|
||||
{
|
||||
if (cost == MONEY32_UNDEFINED)
|
||||
if (res->Error != GA_ERROR::OK)
|
||||
{
|
||||
// If we can't build this, don't show a virtual floor.
|
||||
virtual_floor_set_height(0);
|
||||
|
||||
@@ -170,16 +170,13 @@ extern const LocationXY16 BinUseOffsets[4];
|
||||
extern const LocationXY16 BenchUseOffsets[8];
|
||||
|
||||
TileElement* map_get_footpath_element(int32_t x, int32_t y, int32_t z);
|
||||
struct PathElement;
|
||||
PathElement* map_get_footpath_element_slope(int32_t x, int32_t y, int32_t z, int32_t slope);
|
||||
void footpath_interrupt_peeps(int32_t x, int32_t y, int32_t z);
|
||||
void game_command_place_footpath(
|
||||
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_footpath_from_track(
|
||||
int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp);
|
||||
void game_command_remove_footpath(
|
||||
int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp);
|
||||
money32 footpath_place(int32_t type, int32_t x, int32_t y, int32_t z, int32_t slope, int32_t flags);
|
||||
money32 footpath_place_remove_intersecting(
|
||||
int32_t type, int32_t x, int32_t y, int32_t z, int32_t slope, int32_t flags, int32_t direction);
|
||||
money32 footpath_remove(int32_t x, int32_t y, int32_t z, int32_t flags);
|
||||
money32 footpath_provisional_set(int32_t type, int32_t x, int32_t y, int32_t z, int32_t slope);
|
||||
void footpath_provisional_remove();
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "../Cheats.h"
|
||||
#include "../Context.h"
|
||||
#include "../Game.h"
|
||||
#include "../actions/FootpathSceneryRemoveAction.hpp"
|
||||
#include "../actions/LargeSceneryRemoveAction.hpp"
|
||||
#include "../actions/SmallSceneryRemoveAction.hpp"
|
||||
#include "../actions/WallRemoveAction.hpp"
|
||||
@@ -205,9 +206,9 @@ void scenery_remove_ghost_tool_placement()
|
||||
if (tileElement->base_height != z)
|
||||
continue;
|
||||
|
||||
game_do_command(
|
||||
x, 233 | (gSceneryPlacePathSlope << 8), y, z | (gSceneryPlacePathType << 8), GAME_COMMAND_PLACE_PATH,
|
||||
gSceneryGhostPathObjectType & 0xFFFF0000, 0);
|
||||
auto footpathSceneryRemoveAction = FootpathSceneryRemoveAction({ x, y, z * 8 });
|
||||
footpathSceneryRemoveAction.SetFlags(GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_GHOST);
|
||||
GameActions::Execute(&footpathSceneryRemoveAction);
|
||||
break;
|
||||
} while (!(tileElement++)->IsLastForTile());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user