1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-20 05:23:04 +01:00
Files
OpenRCT2/src/openrct2/world/SmallScenery.cpp
2017-07-27 17:15:56 +02:00

565 lines
16 KiB
C++

#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
/*****************************************************************************
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
*
* OpenRCT2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* A full copy of the GNU General Public License can be found in licence.txt
*****************************************************************************/
#pragma endregion
#include "../network/network.h"
#include "../rct2.h"
#include "../network/network.h"
extern "C"
{
#include "footpath.h"
#include "scenery.h"
#include "map.h"
#include "park.h"
#include "../cheats.h"
#include "../ride/track_design.h"
}
static money32 SmallSceneryRemove(sint16 x, sint16 y, sint8 baseHeight, uint8 quadrant, uint8 sceneryType, uint8 flags)
{
if (!map_is_location_valid(x, y))
{
return MONEY32_UNDEFINED;
}
money32 cost;
rct_scenery_entry *entry = get_small_scenery_entry(sceneryType);
if (entry == (rct_scenery_entry *)-1)
{
log_warning("Invalid game command for scenery removal, scenery_type = %u", sceneryType);
return MONEY32_UNDEFINED;
}
cost = entry->small_scenery.removal_price * 10;
gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING;
gCommandPosition.x = x + 16;
gCommandPosition.y = y + 16;
gCommandPosition.z = baseHeight * 8;
if (!(flags & GAME_COMMAND_FLAG_GHOST) &&
game_is_paused() &&
!gCheatsBuildInPauseMode)
{
gGameCommandErrorText = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED;
return MONEY32_UNDEFINED;
}
if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) &&
!(flags & GAME_COMMAND_FLAG_GHOST) &&
!gCheatsSandboxMode)
{
// Check if allowed to remove item
if (gParkFlags & PARK_FLAGS_FORBID_TREE_REMOVAL)
{
if (entry->small_scenery.height > 64)
{
gGameCommandErrorText = STR_FORBIDDEN_BY_THE_LOCAL_AUTHORITY;
return MONEY32_UNDEFINED;
}
}
// Check if the land is owned
if (!map_is_location_owned(x, y, gCommandPosition.z))
{
return MONEY32_UNDEFINED;
}
}
bool sceneryFound = false;
rct_map_element* mapElement = map_get_first_element_at(x / 32, y / 32);
do {
if ((mapElement->type >> 6) != quadrant)
continue;
if (mapElement->base_height != baseHeight)
continue;
if (mapElement->properties.scenery.type != sceneryType)
continue;
if ((flags & GAME_COMMAND_FLAG_GHOST) &&
!(mapElement->flags & MAP_ELEMENT_FLAG_GHOST))
continue;
sceneryFound = true;
break;
} while (!map_element_is_last_for_tile(mapElement++));
if (sceneryFound == false)
{
return 0;
}
// Remove element
if (flags & GAME_COMMAND_FLAG_APPLY)
{
if (gGameCommandNestLevel == 1 && !(flags & GAME_COMMAND_FLAG_GHOST))
{
rct_xyz16 coord;
coord.x = x + 16;
coord.y = y + 16;
coord.z = map_element_height(coord.x, coord.y);
network_set_player_last_action_coord(network_get_player_index(game_command_playerid), coord);
}
map_invalidate_tile_full(x, y);
map_element_remove(mapElement);
}
return (gParkFlags & PARK_FLAGS_NO_MONEY) ? 0 : cost;
}
static money32 SmallScenerySetColour(sint16 x, sint16 y, sint8 baseHeight, uint8 quadrant, uint8 sceneryType, uint8 primaryColour, uint8 secondaryColour, uint8 flags)
{
gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING;
sint32 z = baseHeight * 8;
gCommandPosition.x = x + 16;
gCommandPosition.y = y + 16;
gCommandPosition.z = z;
if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode)
{
if (!map_is_location_owned(x, y, z))
{
return MONEY32_UNDEFINED;
}
}
rct_map_element *mapElement = map_get_small_scenery_element_at(x, y, baseHeight, sceneryType, quadrant);
if (mapElement == NULL)
{
return 0;
}
if ((flags & GAME_COMMAND_FLAG_GHOST) && !(mapElement->flags & MAP_ELEMENT_FLAG_GHOST))
{
return 0;
}
if (flags & GAME_COMMAND_FLAG_APPLY)
{
mapElement->properties.scenery.colour_1 &= 0xE0;
mapElement->properties.scenery.colour_1 |= primaryColour;
mapElement->properties.scenery.colour_2 &= 0xE0;
mapElement->properties.scenery.colour_2 |= secondaryColour;
map_invalidate_tile_full(x, y);
}
return 0;
}
static money32 SmallSceneryPlace(sint16 x,
sint16 y,
sint16 targetHeight,
uint8 quadrant,
uint8 rotation,
uint8 sceneryType,
uint8 primaryColour,
uint8 secondaryColour,
uint8 flags)
{
gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING;
money32 clearCost = 0;
bool isOnWater = false;
bool supportsRequired = false;
if (targetHeight != 0)
{
supportsRequired = true;
}
sint32 baseHeight = map_element_height(x, y);
// If on water
if (baseHeight & 0xFFFF0000)
{
baseHeight >>= 16;
}
gCommandPosition.x = x;
gCommandPosition.y = y;
gCommandPosition.z = baseHeight;
if (targetHeight != 0)
{
baseHeight = targetHeight;
gCommandPosition.z = baseHeight;
}
gCommandPosition.x += 16;
gCommandPosition.y += 16;
if (game_is_paused() && !gCheatsBuildInPauseMode)
{
gGameCommandErrorText = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED;
return MONEY32_UNDEFINED;
}
if (!map_check_free_elements_and_reorganise(1))
{
return MONEY32_UNDEFINED;
}
if (!byte_9D8150 && (x > gMapSizeMaxXY || y > gMapSizeMaxXY))
{
return MONEY32_UNDEFINED;
}
rct_scenery_entry* sceneryEntry = get_small_scenery_entry(sceneryType);
if (sceneryEntry == NULL)
{
return MONEY32_UNDEFINED;
}
if (sceneryEntry->small_scenery.flags & SMALL_SCENERY_FLAG_FULL_TILE ||
!(sceneryEntry->small_scenery.flags & SMALL_SCENERY_FLAG_DIAGONAL))
{
if (sceneryEntry->small_scenery.flags &
(SMALL_SCENERY_FLAG_DIAGONAL | SMALL_SCENERY_FLAG_HALF_SPACE | SMALL_SCENERY_FLAG_THREE_QUARTERS))
{
quadrant = 0;
}
}
// Check if sub tile height is any different compared to actual surface tile height
sint32 x2 = x;
sint32 y2 = y;
if (sceneryEntry->small_scenery.flags & SMALL_SCENERY_FLAG_FULL_TILE)
{
x2 += 16;
y2 += 16;
}
else
{
x2 += ScenerySubTileOffsets[quadrant & 3].x - 1;
y2 += ScenerySubTileOffsets[quadrant & 3].y - 1;
}
baseHeight = map_element_height(x2, y2);
// If on water
if (baseHeight & 0xFFFF0000)
{
// base_height2 is now the water height
baseHeight >>= 16;
if (targetHeight == 0)
{
isOnWater = true;
}
}
if (targetHeight == 0)
{
targetHeight = baseHeight;
}
if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) &&
!gCheatsSandboxMode &&
!map_is_location_owned(x, y, targetHeight))
{
return MONEY32_UNDEFINED;
}
if (flags & GAME_COMMAND_FLAG_APPLY && !(flags & GAME_COMMAND_FLAG_GHOST))
{
footpath_remove_litter(x, y, targetHeight);
if (!gCheatsDisableClearanceChecks && (sceneryEntry->small_scenery.flags & SMALL_SCENERY_FLAG_ALLOW_WALLS))
{
wall_remove_at(x, y, targetHeight, targetHeight + sceneryEntry->small_scenery.height);
}
}
rct_map_element* surfaceElement = map_get_surface_element_at(x / 32, y / 32);
if (!gCheatsDisableClearanceChecks && map_get_water_height(surfaceElement) > 0)
{
sint32 water_height = (map_get_water_height(surfaceElement) * 16) - 1;
if (water_height > targetHeight)
{
gGameCommandErrorText = STR_CANT_BUILD_THIS_UNDERWATER;
return MONEY32_UNDEFINED;
}
}
if (!gCheatsDisableClearanceChecks && !(sceneryEntry->small_scenery.flags & SMALL_SCENERY_FLAG_STACKABLE))
{
if (isOnWater)
{
gGameCommandErrorText = STR_CAN_ONLY_BUILD_THIS_ON_LAND;
return MONEY32_UNDEFINED;
}
if (map_get_water_height(surfaceElement) > 0)
{
if ((map_get_water_height(surfaceElement) * 16) > targetHeight)
{
gGameCommandErrorText = STR_CAN_ONLY_BUILD_THIS_ON_LAND;
return MONEY32_UNDEFINED;
}
}
}
if (!gCheatsDisableClearanceChecks &&
(sceneryEntry->small_scenery.flags & SMALL_SCENERY_FLAG_REQUIRE_FLAT_SURFACE) &&
!supportsRequired &&
!isOnWater &&
(surfaceElement->properties.surface.slope & 0x1F))
{
gGameCommandErrorText = STR_LEVEL_LAND_REQUIRED;
return MONEY32_UNDEFINED;
}
if (!gCheatsDisableSupportLimits &&
!(sceneryEntry->small_scenery.flags & SMALL_SCENERY_FLAG_STACKABLE) &&
supportsRequired)
{
if (!isOnWater)
{
if (map_get_water_height(surfaceElement) ||
(surfaceElement->base_height * 8) != targetHeight)
{
gGameCommandErrorText = STR_LEVEL_LAND_REQUIRED;
return MONEY32_UNDEFINED;
}
}
else
{
gGameCommandErrorText = STR_CAN_ONLY_BUILD_THIS_ON_LAND;
return MONEY32_UNDEFINED;
}
}
sint32 zLow = targetHeight / 8;
sint32 zHigh = zLow + ceil2(sceneryEntry->small_scenery.height, 8) / 8;
uint8 collisionQuadrants = 0xF;
uint8 blSupports = 0;
if (!(sceneryEntry->small_scenery.flags & SMALL_SCENERY_FLAG_FULL_TILE))
{
collisionQuadrants = 1 << (quadrant ^ 2);
}
if (!(sceneryEntry->small_scenery.flags & SMALL_SCENERY_FLAG_HALF_SPACE))
{
if (sceneryEntry->small_scenery.flags & SMALL_SCENERY_FLAG_DIAGONAL &&
sceneryEntry->small_scenery.flags & SMALL_SCENERY_FLAG_FULL_TILE)
{
if (sceneryEntry->small_scenery.flags & SMALL_SCENERY_FLAG_THREE_QUARTERS)
{
collisionQuadrants = 0xF & rol8(0xBB, ((quadrant ^ 2) + rotation) & 3);
}
else
{
collisionQuadrants = 0xA >> ((quadrant + rotation) & 1);
}
}
}
else
{
collisionQuadrants = 0xF & rol8(0x33, ((quadrant ^ 2) + rotation) & 3);
}
if (!supportsRequired)
{
blSupports |= 0xF0;
}
if (!gCheatsDisableClearanceChecks &&
!map_can_construct_with_clear_at(
x,
y,
zLow,
zHigh,
&map_place_scenery_clear_func,
blSupports | collisionQuadrants,
flags,
&clearCost))
{
return MONEY32_UNDEFINED;
}
gSceneryGroundFlags = gMapGroundFlags & (ELEMENT_IS_ABOVE_GROUND | ELEMENT_IS_UNDERGROUND);
money32 cost = (sceneryEntry->small_scenery.price * 10) + clearCost;
if (gParkFlags & PARK_FLAGS_NO_MONEY)
{
cost = 0;
}
if (!(flags & GAME_COMMAND_FLAG_APPLY))
{
return cost;
}
if (gGameCommandNestLevel == 1 && !(flags & GAME_COMMAND_FLAG_GHOST))
{
rct_xyz16 coord;
coord.x = x + 16;
coord.y = y + 16;
coord.z = map_element_height(coord.x, coord.y);
network_set_player_last_action_coord(network_get_player_index(game_command_playerid), coord);
}
rct_map_element* newElement = map_element_insert(x / 32, y / 32, zLow, collisionQuadrants);
assert(newElement != NULL);
gSceneryMapElement = newElement;
uint8 type = quadrant << 6;
type |= MAP_ELEMENT_TYPE_SCENERY;
type |= rotation;
newElement->type = type;
newElement->properties.scenery.type = sceneryType;
newElement->properties.scenery.age = 0;
newElement->properties.scenery.colour_1 = primaryColour;
newElement->properties.scenery.colour_2 = secondaryColour;
newElement->clearance_height = newElement->base_height + ((sceneryEntry->small_scenery.height + 7) / 8);
if (supportsRequired)
{
newElement->properties.scenery.colour_1 |= 0x20;
}
if (flags & GAME_COMMAND_FLAG_GHOST)
{
newElement->flags |= MAP_ELEMENT_FLAG_GHOST;
}
map_invalidate_tile_full(x, y);
if (sceneryEntry->small_scenery.flags & SMALL_SCENERY_FLAG_ANIMATED)
{
map_animation_create(2, x, y, newElement->base_height);
}
return cost;
}
extern "C"
{
/**
*
* rct2: 0x006E0E01
*/
void game_command_remove_scenery(sint32* eax, sint32* ebx, sint32* ecx, sint32* edx, sint32* esi, sint32* edi, sint32* ebp)
{
*ebx = SmallSceneryRemove(
*eax & 0xFFFF,
*ecx & 0xFFFF,
*edx & 0xFF,
((*ebx >> 8) & 0xFF) >> 6,
(*edx >> 8) & 0xFF,
*ebx & 0xFF
);
}
/**
*
* rct2: 0x006E0F26
*/
void game_command_set_scenery_colour(sint32* eax, sint32* ebx, sint32* ecx, sint32* edx, sint32* esi, sint32* edi, sint32* ebp)
{
*ebx = SmallScenerySetColour(
*eax & 0xFFFF,
*ecx & 0xFFFF,
*edx & 0xFF,
((*ebx >> 8) & 0xFF) >> 6,
(*edx >> 8) & 0xFF,
*ebp & 0xFF,
(*ebp >> 8) & 0xFF,
*ebx & 0xFF
);
}
/**
*
* rct2: 0x006E0D6E, 0x006B8D88
*/
sint32 map_place_scenery_clear_func(rct_map_element** map_element, sint32 x, sint32 y, uint8 flags, money32* price)
{
if (map_element_get_type(*map_element) != MAP_ELEMENT_TYPE_SCENERY)
return 1;
if (!(flags & GAME_COMMAND_FLAG_PATH_SCENERY))
return 1;
rct_scenery_entry* scenery = get_small_scenery_entry((*map_element)->properties.scenery.type);
if (gParkFlags & PARK_FLAGS_FORBID_TREE_REMOVAL)
{
if (scenery->small_scenery.height > 64)
return 1;
}
if (!(gParkFlags & PARK_FLAGS_NO_MONEY))
*price += scenery->small_scenery.removal_price * 10;
if (flags & GAME_COMMAND_FLAG_GHOST)
return 0;
if (!(flags & GAME_COMMAND_FLAG_APPLY))
return 0;
map_invalidate_tile(x, y, (*map_element)->base_height * 8, (*map_element)->clearance_height * 8);
map_element_remove(*map_element);
(*map_element)--;
return 0;
}
/**
*
* rct2: 0x006C5A4F, 0x006CDE57, 0x006A6733, 0x0066637E
*/
sint32 map_place_non_scenery_clear_func(rct_map_element** map_element, sint32 x, sint32 y, uint8 flags, money32* price)
{
if (map_element_get_type(*map_element) != MAP_ELEMENT_TYPE_SCENERY)
return 1;
rct_scenery_entry* scenery = get_small_scenery_entry((*map_element)->properties.scenery.type);
if (gParkFlags & PARK_FLAGS_FORBID_TREE_REMOVAL)
{
if (scenery->small_scenery.height > 64)
return 1;
}
if (!(gParkFlags & PARK_FLAGS_NO_MONEY))
*price += scenery->small_scenery.removal_price * 10;
if (flags & GAME_COMMAND_FLAG_GHOST)
return 0;
if (!(flags & GAME_COMMAND_FLAG_APPLY))
return 0;
map_invalidate_tile(x, y, (*map_element)->base_height * 8, (*map_element)->clearance_height * 8);
map_element_remove(*map_element);
(*map_element)--;
return 0;
}
/**
*
* rct2: 0x006E08F4
*/
void game_command_place_scenery(sint32* eax, sint32* ebx, sint32* ecx, sint32* edx, sint32* esi, sint32* edi, sint32* ebp)
{
*ebx = SmallSceneryPlace(
*eax & 0xFFFF,
*ecx & 0xFFFF,
*ebp & 0xFFFF,
*edx & 0xFF,
*edi & 0xFF,
(*ebx >> 8) & 0xFF,
(*edx >> 8) & 0xFF,
(*edi >> 16) & 0xFF,
*ebx & 0xFF
);
}
}