1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-30 10:15:36 +01:00
Files
OpenRCT2/src/openrct2/world/Scenery.cpp
2020-02-13 21:24:37 +01:00

311 lines
10 KiB
C++

/*****************************************************************************
* 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.
*****************************************************************************/
#include "Scenery.h"
#include "../Cheats.h"
#include "../Context.h"
#include "../Game.h"
#include "../actions/BannerRemoveAction.hpp"
#include "../actions/FootpathSceneryRemoveAction.hpp"
#include "../actions/LargeSceneryRemoveAction.hpp"
#include "../actions/SmallSceneryRemoveAction.hpp"
#include "../actions/WallRemoveAction.hpp"
#include "../common.h"
#include "../localisation/Localisation.h"
#include "../network/network.h"
#include "../object/ObjectList.h"
#include "../object/ObjectManager.h"
#include "../scenario/Scenario.h"
#include "Climate.h"
#include "Footpath.h"
#include "Fountain.h"
#include "Map.h"
#include "Park.h"
#include "SmallScenery.h"
#include "Wall.h"
uint8_t gSceneryQuadrant;
money32 gSceneryPlaceCost;
int16_t gSceneryPlaceObject;
int16_t gSceneryPlaceZ;
uint8_t gSceneryPlaceRotation;
uint8_t gSceneryGhostType;
LocationXYZ16 gSceneryGhostPosition;
uint8_t gSceneryGhostWallRotation;
int16_t gSceneryShiftPressed;
int16_t gSceneryShiftPressX;
int16_t gSceneryShiftPressY;
int16_t gSceneryShiftPressZOffset;
int16_t gSceneryCtrlPressed;
int16_t gSceneryCtrlPressZ;
money32 gClearSceneryCost;
// rct2: 0x009A3E74
const CoordsXY SceneryQuadrantOffsets[] = { { 7, 7 }, { 7, 23 }, { 23, 23 }, { 23, 7 } };
void scenery_update_tile(const CoordsXY& sceneryPos)
{
TileElement* tileElement;
tileElement = map_get_first_element_at(sceneryPos);
if (tileElement == nullptr)
return;
do
{
// Ghosts are purely this-client-side and should not cause any interaction,
// as that may lead to a desync.
if (network_get_mode() != NETWORK_MODE_NONE)
{
if (tileElement->IsGhost())
continue;
}
if (tileElement->GetType() == TILE_ELEMENT_TYPE_SMALL_SCENERY)
{
scenery_update_age(sceneryPos, tileElement);
}
else if (tileElement->GetType() == TILE_ELEMENT_TYPE_PATH)
{
if (tileElement->AsPath()->HasAddition() && !tileElement->AsPath()->AdditionIsGhost())
{
rct_scenery_entry* sceneryEntry = tileElement->AsPath()->GetAdditionEntry();
if (sceneryEntry != nullptr)
{
if (sceneryEntry->path_bit.flags & PATH_BIT_FLAG_JUMPING_FOUNTAIN_WATER)
{
JumpingFountain::StartAnimation(JUMPING_FOUNTAIN_TYPE_WATER, sceneryPos, tileElement);
}
else if (sceneryEntry->path_bit.flags & PATH_BIT_FLAG_JUMPING_FOUNTAIN_SNOW)
{
JumpingFountain::StartAnimation(JUMPING_FOUNTAIN_TYPE_SNOW, sceneryPos, tileElement);
}
}
}
}
} while (!(tileElement++)->IsLastForTile());
}
/**
*
* rct2: 0x006E33D9
*/
void scenery_update_age(const CoordsXY& sceneryPos, TileElement* tileElement)
{
TileElement* tileElementAbove;
rct_scenery_entry* sceneryEntry;
sceneryEntry = tileElement->AsSmallScenery()->GetEntry();
if (sceneryEntry == nullptr)
{
return;
}
if (gCheatsDisablePlantAging && (scenery_small_entry_has_flag(sceneryEntry, SMALL_SCENERY_FLAG_CAN_BE_WATERED)))
{
return;
}
if (!scenery_small_entry_has_flag(sceneryEntry, SMALL_SCENERY_FLAG_CAN_BE_WATERED)
|| (gClimateCurrent.Weather < WEATHER_RAIN) || (tileElement->AsSmallScenery()->GetAge() < 5))
{
tileElement->AsSmallScenery()->IncreaseAge(sceneryPos);
return;
}
// Check map elements above, presumably to see if map element is blocked from rain
tileElementAbove = tileElement;
// Change from original: RCT2 only checked for the first three quadrants, which was very likely to be a bug.
while (!(tileElementAbove->GetOccupiedQuadrants()))
{
tileElementAbove++;
// Ghosts are purely this-client-side and should not cause any interaction,
// as that may lead to a desync.
if (tileElementAbove->IsGhost())
continue;
switch (tileElementAbove->GetType())
{
case TILE_ELEMENT_TYPE_LARGE_SCENERY:
case TILE_ELEMENT_TYPE_ENTRANCE:
case TILE_ELEMENT_TYPE_PATH:
map_invalidate_tile_zoom1({ sceneryPos, tileElementAbove->GetBaseZ(), tileElementAbove->GetClearanceZ() });
tileElement->AsSmallScenery()->IncreaseAge(sceneryPos);
return;
case TILE_ELEMENT_TYPE_SMALL_SCENERY:
sceneryEntry = tileElementAbove->AsSmallScenery()->GetEntry();
if (scenery_small_entry_has_flag(sceneryEntry, SMALL_SCENERY_FLAG_VOFFSET_CENTRE))
{
tileElement->AsSmallScenery()->IncreaseAge(sceneryPos);
return;
}
break;
}
}
// Reset age / water plant
tileElement->AsSmallScenery()->SetAge(0);
map_invalidate_tile_zoom1({ sceneryPos, tileElement->GetBaseZ(), tileElement->GetClearanceZ() });
}
/**
*
* rct2: 0x006E2712
*/
void scenery_remove_ghost_tool_placement()
{
int16_t x, y, z;
x = gSceneryGhostPosition.x;
y = gSceneryGhostPosition.y;
z = gSceneryGhostPosition.z;
if (gSceneryGhostType & SCENERY_GHOST_FLAG_0)
{
gSceneryGhostType &= ~SCENERY_GHOST_FLAG_0;
auto removeSceneryAction = SmallSceneryRemoveAction({ x, y, z * 8 }, gSceneryQuadrant, gSceneryPlaceObject);
removeSceneryAction.SetFlags(
GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND | GAME_COMMAND_FLAG_GHOST);
removeSceneryAction.Execute();
}
if (gSceneryGhostType & SCENERY_GHOST_FLAG_1)
{
gSceneryGhostType &= ~SCENERY_GHOST_FLAG_1;
TileElement* tileElement = map_get_first_element_at({ x, y });
do
{
if (tileElement == nullptr)
break;
if (tileElement->GetType() != TILE_ELEMENT_TYPE_PATH)
continue;
if (tileElement->base_height != z)
continue;
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());
}
if (gSceneryGhostType & SCENERY_GHOST_FLAG_2)
{
gSceneryGhostType &= ~SCENERY_GHOST_FLAG_2;
CoordsXYZD wallLocation = { x, y, z * 8, gSceneryGhostWallRotation };
auto wallRemoveAction = WallRemoveAction(wallLocation);
wallRemoveAction.SetFlags(GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_PATH_SCENERY);
wallRemoveAction.Execute();
}
if (gSceneryGhostType & SCENERY_GHOST_FLAG_3)
{
gSceneryGhostType &= ~SCENERY_GHOST_FLAG_3;
auto removeSceneryAction = LargeSceneryRemoveAction({ x, y, z * 8, gSceneryPlaceRotation }, 0);
removeSceneryAction.SetFlags(
GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED
| GAME_COMMAND_FLAG_NO_SPEND);
removeSceneryAction.Execute();
}
if (gSceneryGhostType & SCENERY_GHOST_FLAG_4)
{
gSceneryGhostType &= ~SCENERY_GHOST_FLAG_4;
auto removeSceneryAction = BannerRemoveAction({ x, y, z * 8, gSceneryPlaceRotation });
removeSceneryAction.SetFlags(
GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND);
GameActions::Execute(&removeSceneryAction);
}
}
rct_scenery_entry* get_wall_entry(int32_t entryIndex)
{
rct_scenery_entry* result = nullptr;
auto& objMgr = OpenRCT2::GetContext()->GetObjectManager();
auto obj = objMgr.GetLoadedObject(OBJECT_TYPE_WALLS, entryIndex);
if (obj != nullptr)
{
result = (rct_scenery_entry*)obj->GetLegacyData();
}
return result;
}
rct_scenery_entry* get_banner_entry(int32_t entryIndex)
{
rct_scenery_entry* result = nullptr;
auto& objMgr = OpenRCT2::GetContext()->GetObjectManager();
auto obj = objMgr.GetLoadedObject(OBJECT_TYPE_BANNERS, entryIndex);
if (obj != nullptr)
{
result = (rct_scenery_entry*)obj->GetLegacyData();
}
return result;
}
rct_scenery_entry* get_footpath_item_entry(int32_t entryIndex)
{
rct_scenery_entry* result = nullptr;
auto& objMgr = OpenRCT2::GetContext()->GetObjectManager();
auto obj = objMgr.GetLoadedObject(OBJECT_TYPE_PATH_BITS, entryIndex);
if (obj != nullptr)
{
result = (rct_scenery_entry*)obj->GetLegacyData();
}
return result;
}
rct_scenery_group_entry* get_scenery_group_entry(int32_t entryIndex)
{
rct_scenery_group_entry* result = nullptr;
auto& objMgr = OpenRCT2::GetContext()->GetObjectManager();
auto obj = objMgr.GetLoadedObject(OBJECT_TYPE_SCENERY_GROUP, entryIndex);
if (obj != nullptr)
{
result = (rct_scenery_group_entry*)obj->GetLegacyData();
}
return result;
}
int32_t get_scenery_id_from_entry_index(uint8_t objectType, int32_t entryIndex)
{
switch (objectType)
{
case OBJECT_TYPE_SMALL_SCENERY:
return entryIndex + SCENERY_SMALL_SCENERY_ID_MIN;
case OBJECT_TYPE_PATH_BITS:
return entryIndex + SCENERY_PATH_SCENERY_ID_MIN;
case OBJECT_TYPE_WALLS:
return entryIndex + SCENERY_WALLS_ID_MIN;
case OBJECT_TYPE_LARGE_SCENERY:
return entryIndex + SCENERY_LARGE_SCENERY_ID_MIN;
case OBJECT_TYPE_BANNERS:
return entryIndex + SCENERY_BANNERS_ID_MIN;
default:
return -1;
}
}
int32_t wall_entry_get_door_sound(const rct_scenery_entry* wallEntry)
{
return (wallEntry->wall.flags2 & WALL_SCENERY_2_DOOR_SOUND_MASK) >> WALL_SCENERY_2_DOOR_SOUND_SHIFT;
}